@theoremjs/react 0.3.0 → 0.3.1
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 +728 -202
- package/client/composer-hints.js +1 -1
- package/client/decision-images.d.ts +76 -0
- package/client/decision-images.js +139 -0
- package/client/decision-transport.d.ts +26 -4
- package/client/decision-transport.js +79 -6
- package/client/embedding-input.d.ts +84 -0
- package/client/embedding-input.js +194 -0
- package/client/embedding-space.d.ts +82 -0
- package/client/embedding-space.js +389 -0
- package/client/embedding-transport.d.ts +40 -0
- package/client/embedding-transport.js +71 -0
- package/client/failure.d.ts +7 -10
- package/client/failure.js +9 -9
- package/client/host-call.d.ts +1 -1
- package/client/host-call.js +7 -2
- package/client/host-transport.d.ts +2 -0
- package/client/host-transport.js +3 -2
- package/client/index.d.ts +19 -11
- package/client/index.js +14 -10
- package/client/ink-waveform.d.ts +1 -1
- package/client/ink-waveform.js +1 -2
- package/client/live/apply-live-turn-tool-event.js +1 -1
- package/client/live/live-captions.d.ts +30 -0
- package/client/live/live-captions.js +105 -1
- package/client/live/live-mic-forward.d.ts +1 -0
- package/client/live/live-mic-forward.js +11 -2
- package/client/live/live-page-tool.d.ts +34 -0
- package/client/live/live-page-tool.js +11 -0
- package/client/live/live-state.d.ts +4 -2
- package/client/live/live-state.js +2 -0
- package/client/live/live-video.js +1 -1
- package/client/live/run-live-tool-call.d.ts +7 -2
- package/client/live/run-live-tool-call.js +48 -10
- package/client/live/use-live-caption-log.d.ts +17 -0
- package/client/live/use-live-caption-log.js +51 -0
- package/client/live-client.d.ts +52 -4
- package/client/live-client.js +250 -59
- package/client/live-messages.d.ts +51 -13
- package/client/live-messages.js +42 -9
- package/client/page-dispatch.d.ts +15 -0
- package/client/page-dispatch.js +63 -0
- package/client/run-commit.d.ts +16 -43
- package/client/run-commit.js +26 -101
- package/client/run-session.d.ts +40 -41
- package/client/run-session.js +104 -112
- package/client/schema-fields.js +27 -6
- package/client/shaped-data.js +37 -8
- package/client/source-citations.js +1 -1
- package/client/space-geometry.d.ts +39 -0
- package/client/space-geometry.js +183 -0
- package/client/state-fields.js +1 -1
- package/client/tool-result.js +30 -12
- package/client/tool-resume.d.ts +18 -1
- package/client/tool-resume.js +4 -1
- package/client/trace-feed.d.ts +1 -1
- package/client/trace-story.d.ts +2 -2
- package/client/trace-story.js +43 -19
- package/client/trace-view.d.ts +2 -2
- package/client/trace-view.js +56 -16
- package/client/transcript-groups.d.ts +28 -2
- package/client/transcript-groups.js +63 -13
- package/client/transcript-schema.d.ts +10 -0
- package/client/transcript-schema.js +120 -0
- package/client/transport.d.ts +114 -26
- package/client/transport.js +106 -32
- package/client/turn-client.d.ts +17 -8
- package/client/turn-client.js +26 -31
- package/client/voice-recorder.d.ts +1 -1
- package/client/voice-recorder.js +2 -5
- package/client/wire-line.js +5 -5
- package/client/worklets/mic-capture.js +9 -9
- package/components/InkWaveform.d.ts +12 -1
- package/components/InkWaveform.js +65 -42
- package/components/live/use-live-runner-controls.d.ts +3 -4
- package/components/live/use-live-runner-controls.js +6 -10
- package/components/live/use-live-runner-model.d.ts +10 -6
- package/components/live/use-live-runner-model.js +29 -14
- package/components/live/use-live-runner-ui.d.ts +10 -8
- package/components/live/use-live-runner-ui.js +19 -11
- package/components/live/use-live-session-client.d.ts +4 -4
- package/components/live/use-live-session-client.js +11 -8
- package/components/use-composer-voice.d.ts +4 -4
- package/components/use-composer-voice.js +8 -8
- package/components/use-editor-selection.js +4 -1
- package/components/use-voice-playback.d.ts +2 -1
- package/components/use-voice-playback.js +4 -4
- package/hooks/use-described.d.ts +5 -1
- package/hooks/use-described.js +11 -9
- package/hooks/use-embedding-index.d.ts +27 -0
- package/hooks/use-embedding-index.js +43 -0
- package/hooks/use-one-call.d.ts +31 -0
- package/hooks/use-one-call.js +76 -0
- package/hooks/use-page-tool-warnings.d.ts +6 -0
- package/hooks/use-page-tool-warnings.js +25 -0
- package/hooks/use-second-ticker.d.ts +2 -0
- package/hooks/use-second-ticker.js +13 -0
- package/hooks/use-theorem-chat-actions.d.ts +25 -21
- package/hooks/use-theorem-chat-actions.js +67 -45
- package/hooks/use-theorem-chat-state.d.ts +6 -6
- package/hooks/use-theorem-chat-state.js +13 -9
- package/hooks/use-theorem-chat.d.ts +48 -14
- package/hooks/use-theorem-chat.js +329 -141
- package/hooks/use-theorem-decision.d.ts +5 -10
- package/hooks/use-theorem-decision.js +4 -67
- package/hooks/use-theorem-embedding.d.ts +18 -0
- package/hooks/use-theorem-embedding.js +12 -0
- package/hooks/use-theorem-host.d.ts +5 -4
- package/hooks/use-theorem-host.js +26 -8
- package/hooks/use-theorem-interface.d.ts +6 -3
- package/hooks/use-theorem-interface.js +12 -22
- package/index.d.ts +6 -3
- package/index.js +5 -2
- package/live.d.ts +1 -0
- package/live.js +1 -1
- package/package.json +6 -5
- package/server/checkpoint-ownership.d.ts +6 -0
- package/server/checkpoint-ownership.js +42 -0
- package/server/conversation-store.d.ts +92 -0
- package/server/conversation-store.js +131 -0
- package/server/conversation.d.ts +193 -0
- package/server/conversation.js +552 -0
- package/server/decision-handler.d.ts +12 -4
- package/server/decision-handler.js +29 -38
- package/server/embedding-handler.d.ts +33 -0
- package/server/embedding-handler.js +45 -0
- package/server/handler.d.ts +58 -20
- package/server/handler.js +441 -238
- package/server/mod.d.ts +15 -6
- package/server/mod.js +13 -5
- package/server/page-dispatch.d.ts +22 -0
- package/server/page-dispatch.js +127 -0
- package/server/request-body.d.ts +2 -0
- package/server/request-body.js +42 -0
- package/server/request-check.d.ts +6 -1
- package/server/request-check.js +27 -12
- package/server/session-authority.d.ts +8 -0
- package/server/session-authority.js +78 -0
- package/server/session-boundary.d.ts +26 -0
- package/server/session-boundary.js +64 -0
- package/server/session-identity.d.ts +16 -0
- package/server/session-identity.js +41 -0
- package/server/session-limits.d.ts +12 -0
- package/server/session-limits.js +59 -0
- package/server/session-store.d.ts +69 -20
- package/server/session-store.js +220 -5
- package/server/single-action.d.ts +21 -0
- package/server/single-action.js +30 -0
- package/server/steer-inbox.js +1 -1
- package/server/tool-operations.d.ts +6 -0
- package/server/tool-operations.js +132 -0
- package/server/turn-input.d.ts +10 -0
- package/server/turn-input.js +29 -0
- package/styles/ink-controls.css +78 -78
- package/ui/CardWall.d.ts +10 -0
- package/ui/CardWall.js +61 -0
- package/ui/ChatComposerBar.d.ts +28 -5
- package/ui/ChatComposerBar.js +85 -30
- package/ui/ChatTranscript.d.ts +14 -1
- package/ui/ChatTranscript.js +110 -57
- package/ui/ConsoleFrame.d.ts +61 -4
- package/ui/ConsoleFrame.js +109 -9
- package/ui/DecisionAnswers.d.ts +40 -2
- package/ui/DecisionAnswers.js +166 -35
- package/ui/DecisionImages.d.ts +16 -0
- package/ui/DecisionImages.js +46 -0
- package/ui/EmbeddingDocuments.d.ts +78 -0
- package/ui/EmbeddingDocuments.js +278 -0
- package/ui/EmbeddingQuery.d.ts +27 -0
- package/ui/EmbeddingQuery.js +95 -0
- package/ui/EmbeddingResults.d.ts +41 -0
- package/ui/EmbeddingResults.js +165 -0
- package/ui/EmbeddingSpace.d.ts +26 -0
- package/ui/EmbeddingSpace.js +106 -0
- package/ui/LiveCaptionsPanel.d.ts +22 -0
- package/ui/LiveCaptionsPanel.js +27 -0
- package/ui/LiveRunner.d.ts +6 -4
- package/ui/LiveRunner.js +42 -55
- package/ui/PaneState.d.ts +33 -0
- package/ui/PaneState.js +50 -0
- package/ui/SchemaFields.d.ts +1 -1
- package/ui/SchemaFields.js +54 -26
- package/ui/ShapedData.js +73 -28
- package/ui/SidePanel.d.ts +74 -5
- package/ui/SidePanel.js +82 -9
- package/ui/StateFields.d.ts +10 -2
- package/ui/StateFields.js +56 -13
- package/ui/TheoremChat.d.ts +65 -14
- package/ui/TheoremChat.js +66 -78
- package/ui/TheoremDecision.d.ts +12 -20
- package/ui/TheoremDecision.js +78 -35
- package/ui/TheoremEmbedding.d.ts +27 -0
- package/ui/TheoremEmbedding.js +122 -0
- package/ui/TheoremHost.d.ts +22 -5
- package/ui/TheoremHost.js +127 -37
- package/ui/ToolGateCard.d.ts +5 -1
- package/ui/ToolGateCard.js +30 -23
- package/ui/ToolResult.js +12 -10
- package/ui/ToolResultCharts.js +29 -12
- package/ui/TraceCharts.d.ts +2 -2
- package/ui/TraceCharts.js +48 -17
- package/ui/TraceGuardrails.d.ts +3 -2
- package/ui/TraceGuardrails.js +24 -6
- package/ui/TraceInspector.d.ts +11 -1
- package/ui/TraceInspector.js +45 -28
- package/ui/TraceInspectorPanel.d.ts +31 -9
- package/ui/TraceInspectorPanel.js +77 -39
- package/ui/TraceOverview.d.ts +7 -3
- package/ui/TraceOverview.js +44 -14
- package/ui/TraceSpanDetail.js +24 -22
- package/ui/TraceStory.js +9 -8
- package/ui/TraceValues.js +19 -9
- package/ui/TraceWaterfall.js +12 -9
- package/ui/VoiceNote.js +4 -4
- package/ui/approval-heading.d.ts +4 -0
- package/ui/approval-heading.js +9 -0
- package/ui/arrive.d.ts +18 -0
- package/ui/arrive.js +46 -0
- package/ui/built/theme.css +86 -68
- package/ui/built/theme.css.d.ts +9 -0
- package/ui/built/theorem.d.ts +3 -3
- package/ui/built/theorem.js +34 -5
- package/ui/built/theorem.variants.d.ts +8 -2
- package/ui/code-language.d.ts +12 -0
- package/ui/code-language.js +50 -0
- package/ui/disclosure-motion.js +53 -9
- package/ui/embedding-space-gl.d.ts +37 -0
- package/ui/embedding-space-gl.js +663 -0
- package/ui/embedding-space-view.d.ts +8 -0
- package/ui/embedding-space-view.js +8 -0
- package/ui/glide.d.ts +7 -0
- package/ui/glide.js +44 -0
- package/ui/icons.js +2 -1
- package/ui/index.d.ts +20 -9
- package/ui/index.js +20 -10
- package/ui/labels-provider.js +5 -2
- package/ui/labels.d.ts +374 -85
- package/ui/labels.js +1028 -188
- package/ui/markdown-plugins.d.ts +9 -0
- package/ui/markdown-plugins.js +13 -0
- package/ui/mcp-icon.d.ts +6 -0
- package/ui/mcp-icon.js +8 -0
- package/ui/row-keys.d.ts +19 -0
- package/ui/row-keys.js +33 -0
- package/ui/theme.d.ts +1 -1
- package/ui/theme.js +2 -2
- package/ui/theorem-theme.js +32 -10
- package/ui/trace-palette.d.ts +2 -16
- package/ui/trace-palette.js +2 -16
- package/ui/trace-patterns.d.ts +11 -3
- package/ui/trace-patterns.js +16 -3
- package/ui/transcript-copy-text.js +1 -0
- package/ui/wall-columns.d.ts +8 -0
- package/ui/wall-columns.js +19 -0
- package/client/walk-away.d.ts +0 -34
- package/client/walk-away.js +0 -48
- package/server/walk-away.d.ts +0 -22
- package/server/walk-away.js +0 -41
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the embedding space draws, as flat arrays a GPU takes as they are:
|
|
3
|
+
* the axes' three circles, each document's dot, a line for each link, heavier
|
|
4
|
+
* the further its cosine similarity passes the threshold, and a query's white
|
|
5
|
+
* dot with a line to each match that passes its own.
|
|
6
|
+
* Every other line joins two real points and stands for their cosine
|
|
7
|
+
* similarity; nothing is drawn for show.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Floats per line segment: from (3), to (3), colour (3), alpha, width in CSS
|
|
13
|
+
* pixels, the two slots it reads its state from, where it sits along its line
|
|
14
|
+
* (0 at the start, 1 at the end), and the gap in CSS pixels it leaves before
|
|
15
|
+
* its start and after its end, where a dot sits.
|
|
16
|
+
*/
|
|
17
|
+
export const SEGMENT_FLOATS = 16;
|
|
18
|
+
/** Floats per dot: position (3), colour (3), alpha, radius in CSS pixels, its slot, and where it sits along the line that reaches it. */
|
|
19
|
+
export const DOT_FLOATS = 10;
|
|
20
|
+
/** Stands for the query where a slot would go. */
|
|
21
|
+
export const QUERY_SLOT = -1;
|
|
22
|
+
/** Stands for the axes' circles, which no document's state reaches. */
|
|
23
|
+
export const FRAME_SLOT = -2;
|
|
24
|
+
/** Every cluster's colour has this lightness and chroma, so none reads dirtier or louder than another. */
|
|
25
|
+
const LIGHTNESS = 0.72;
|
|
26
|
+
const CHROMA = 0.085;
|
|
27
|
+
const QUERY_COLOUR = [0.97, 0.98, 1.0];
|
|
28
|
+
/**
|
|
29
|
+
* Every line is a faint hairline in its documents' colour: it hints at a
|
|
30
|
+
* connection, and the dots carry the picture. Its width in CSS pixels and its
|
|
31
|
+
* opacity, at the threshold and at identical.
|
|
32
|
+
*/
|
|
33
|
+
const LINE_WIDTH = [0.3, 0.5];
|
|
34
|
+
const LINE_ALPHA = [0.18, 0.4];
|
|
35
|
+
function weighted([at, to], strength) {
|
|
36
|
+
return at + (to - at) * strength;
|
|
37
|
+
}
|
|
38
|
+
function gamma(linear) {
|
|
39
|
+
return linear <= 0.0031308 ? 12.92 * linear : 1.055 * linear ** (1 / 2.4) - 0.055;
|
|
40
|
+
}
|
|
41
|
+
/** An OKLCH colour as sRGB from 0 to 1, or `null` when the screen cannot show it. */
|
|
42
|
+
function oklch(l, c, hue) {
|
|
43
|
+
const a = c * Math.cos(hue);
|
|
44
|
+
const b = c * Math.sin(hue);
|
|
45
|
+
const l_ = (l + 0.3963377774 * a + 0.2158037573 * b) ** 3;
|
|
46
|
+
const m_ = (l - 0.1055613458 * a - 0.0638541728 * b) ** 3;
|
|
47
|
+
const s_ = (l - 0.0894841775 * a - 1.291485548 * b) ** 3;
|
|
48
|
+
const rgb = [
|
|
49
|
+
4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_,
|
|
50
|
+
-1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_,
|
|
51
|
+
-0.0041960863 * l_ - 0.7034186147 * m_ + 1.707614701 * s_,
|
|
52
|
+
];
|
|
53
|
+
if (rgb.some((v) => v < -1e-4 || v > 1 + 1e-4))
|
|
54
|
+
return null;
|
|
55
|
+
return rgb.map((v) => gamma(Math.min(1, Math.max(0, v))));
|
|
56
|
+
}
|
|
57
|
+
/** A cluster's colour from its tone, a hue from 0 to 1, at the palette's one lightness; chroma gives way only where the screen runs out. */
|
|
58
|
+
function colourOf(tone) {
|
|
59
|
+
const hue = tone * 2 * Math.PI;
|
|
60
|
+
for (let c = CHROMA; c > 0; c -= 0.005) {
|
|
61
|
+
const colour = oklch(LIGHTNESS, c, hue);
|
|
62
|
+
if (colour)
|
|
63
|
+
return colour;
|
|
64
|
+
}
|
|
65
|
+
return oklch(LIGHTNESS, 0, hue) ?? [0.8, 0.8, 0.8];
|
|
66
|
+
}
|
|
67
|
+
function mixed(a, b, u) {
|
|
68
|
+
return [a[0] + (b[0] - a[0]) * u, a[1] + (b[1] - a[1]) * u, a[2] + (b[2] - a[2]) * u];
|
|
69
|
+
}
|
|
70
|
+
/** The longest piece a line is cut into, so depth fades along it, not only at its ends. */
|
|
71
|
+
const PIECE = 0.1;
|
|
72
|
+
/** Points along the straight line from one point to another, through the ball. */
|
|
73
|
+
function pieces(a, b) {
|
|
74
|
+
const steps = Math.max(1, Math.ceil(Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]) / PIECE));
|
|
75
|
+
return Array.from({ length: steps + 1 }, (_, s) => mixed(a, b, s / steps));
|
|
76
|
+
}
|
|
77
|
+
class Strokes {
|
|
78
|
+
segments = [];
|
|
79
|
+
dots = [];
|
|
80
|
+
line({ from, to, colour, alpha, width, slots, gaps }) {
|
|
81
|
+
const points = pieces(from, to);
|
|
82
|
+
const last = points.length - 1;
|
|
83
|
+
for (let i = 1; i <= last; i += 1) {
|
|
84
|
+
const u = i / last;
|
|
85
|
+
this.segments.push(...points[i - 1], ...points[i], ...colour((i - 0.5) / last), alpha, width, slots[0], slots[1], u, i === 1 ? gaps[0] : 0, i === last ? gaps[1] : 0);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** One segment no document's state reaches, always shown, with no gaps. */
|
|
89
|
+
segment(from, to, colour, alpha, width) {
|
|
90
|
+
this.segments.push(...from, ...to, ...colour, alpha, width, FRAME_SLOT, FRAME_SLOT, 0, 0, 0);
|
|
91
|
+
}
|
|
92
|
+
dot({ at, colour, alpha, radius, slot, along }) {
|
|
93
|
+
this.dots.push(...at, ...colour, alpha, radius, slot, along);
|
|
94
|
+
}
|
|
95
|
+
done() {
|
|
96
|
+
return { segments: new Float32Array(this.segments), dots: new Float32Array(this.dots) };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** A document's dot, and the query's, a little larger, in CSS pixels. */
|
|
100
|
+
const DOCUMENT_RADIUS = 2.6;
|
|
101
|
+
const QUERY_RADIUS = 3.6;
|
|
102
|
+
/** The clear space between a line's end and the dot it reaches. */
|
|
103
|
+
const GAP = 1.75;
|
|
104
|
+
/** How far a link passes the threshold, from 0 at it to 1 at identical. */
|
|
105
|
+
function strengthOf(space, similarity) {
|
|
106
|
+
if (space.threshold >= 1)
|
|
107
|
+
return 1;
|
|
108
|
+
return Math.min(Math.max((similarity - space.threshold) / (1 - space.threshold), 0), 1);
|
|
109
|
+
}
|
|
110
|
+
/** The axes' circles: a hairline round the rim in each plane two of the three axes span. */
|
|
111
|
+
const FRAME_COLOUR = [0.88, 0.9, 0.94];
|
|
112
|
+
/** At the ball's front; the renderer fades a circle with the square of its depth, toward a quarter of this at the back. */
|
|
113
|
+
const FRAME_ALPHA = 0.29;
|
|
114
|
+
const FRAME_WIDTH = 0.7;
|
|
115
|
+
const FRAME_STEPS = 96;
|
|
116
|
+
function frame(strokes) {
|
|
117
|
+
const planes = [
|
|
118
|
+
(a) => [Math.cos(a), Math.sin(a), 0],
|
|
119
|
+
(a) => [Math.cos(a), 0, Math.sin(a)],
|
|
120
|
+
(a) => [0, Math.cos(a), Math.sin(a)],
|
|
121
|
+
];
|
|
122
|
+
for (const at of planes)
|
|
123
|
+
for (let i = 0; i < FRAME_STEPS; i += 1)
|
|
124
|
+
strokes.segment(at((i / FRAME_STEPS) * 2 * Math.PI), at(((i + 1) / FRAME_STEPS) * 2 * Math.PI), FRAME_COLOUR, FRAME_ALPHA, FRAME_WIDTH);
|
|
125
|
+
}
|
|
126
|
+
/** Everything a space draws before any query: the axes' circles, a line for each link, then every document's dot. */
|
|
127
|
+
export function spaceGeometry(space) {
|
|
128
|
+
const strokes = new Strokes();
|
|
129
|
+
frame(strokes);
|
|
130
|
+
const colourOfDocument = (index) => colourOf(space.clusters[space.documents[index].cluster].tone);
|
|
131
|
+
for (const link of space.links) {
|
|
132
|
+
const strength = strengthOf(space, link.similarity);
|
|
133
|
+
// invariant: a link's two documents are in one cluster, so it takes their colour.
|
|
134
|
+
const colour = colourOfDocument(link.a);
|
|
135
|
+
strokes.line({
|
|
136
|
+
from: space.documents[link.a].position,
|
|
137
|
+
to: space.documents[link.b].position,
|
|
138
|
+
colour: () => colour,
|
|
139
|
+
alpha: weighted(LINE_ALPHA, strength),
|
|
140
|
+
width: weighted(LINE_WIDTH, strength),
|
|
141
|
+
slots: [link.a, link.b],
|
|
142
|
+
gaps: [DOCUMENT_RADIUS + GAP, DOCUMENT_RADIUS + GAP],
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
for (const document of space.documents)
|
|
146
|
+
strokes.dot({
|
|
147
|
+
at: document.position,
|
|
148
|
+
colour: colourOfDocument(document.index),
|
|
149
|
+
alpha: 1,
|
|
150
|
+
radius: DOCUMENT_RADIUS,
|
|
151
|
+
slot: document.index,
|
|
152
|
+
along: 0,
|
|
153
|
+
});
|
|
154
|
+
return strokes.done();
|
|
155
|
+
}
|
|
156
|
+
/** A query's white dot where it landed, and a line from it to each match, weighted as a link is. */
|
|
157
|
+
export function queryGeometry(space, at, matches) {
|
|
158
|
+
const strokes = new Strokes();
|
|
159
|
+
for (const match of matches) {
|
|
160
|
+
const document = space.documents[match.index];
|
|
161
|
+
if (!document)
|
|
162
|
+
continue;
|
|
163
|
+
const colour = colourOf(space.clusters[document.cluster].tone);
|
|
164
|
+
strokes.line({
|
|
165
|
+
from: at,
|
|
166
|
+
to: document.position,
|
|
167
|
+
colour: (u) => mixed(QUERY_COLOUR, colour, u),
|
|
168
|
+
alpha: weighted(LINE_ALPHA, match.closeness),
|
|
169
|
+
width: weighted(LINE_WIDTH, match.closeness),
|
|
170
|
+
slots: [QUERY_SLOT, match.index],
|
|
171
|
+
gaps: [QUERY_RADIUS + GAP, DOCUMENT_RADIUS + GAP],
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
strokes.dot({
|
|
175
|
+
at,
|
|
176
|
+
colour: QUERY_COLOUR,
|
|
177
|
+
alpha: 1,
|
|
178
|
+
radius: QUERY_RADIUS,
|
|
179
|
+
slot: QUERY_SLOT,
|
|
180
|
+
along: 0,
|
|
181
|
+
});
|
|
182
|
+
return strokes.done();
|
|
183
|
+
}
|
package/client/state-fields.js
CHANGED
|
@@ -35,7 +35,7 @@ export function tableColumns(value) {
|
|
|
35
35
|
Object.values(row).every((cell) => cell === null || typeof cell !== 'object'));
|
|
36
36
|
if (!alike)
|
|
37
37
|
return null;
|
|
38
|
-
// Wider columns for longer values: a role stays narrow beside its message.
|
|
38
|
+
// why: Wider columns for longer values: a role stays narrow beside its message.
|
|
39
39
|
return keys.map((key) => {
|
|
40
40
|
const lengths = value.map((row) => String(row[key] ?? '').length);
|
|
41
41
|
const longest = Math.max(key.length, ...lengths);
|
package/client/tool-result.js
CHANGED
|
@@ -57,8 +57,10 @@ function figuresOf(row, units) {
|
|
|
57
57
|
if (reading)
|
|
58
58
|
figures.push({ key, value, ...reading });
|
|
59
59
|
}
|
|
60
|
-
// Numbers with a unit say more at a glance: they lead.
|
|
61
|
-
return figures
|
|
60
|
+
// why: Numbers with a unit say more at a glance: they lead.
|
|
61
|
+
return figures
|
|
62
|
+
.sort((a, b) => Number(b.unit !== undefined) - Number(a.unit !== undefined))
|
|
63
|
+
.slice(0, MAX_FIGURES);
|
|
62
64
|
}
|
|
63
65
|
/**
|
|
64
66
|
* The figures: the numbers of the first nested object of plain fields (the
|
|
@@ -87,7 +89,9 @@ function distinct(rows, key) {
|
|
|
87
89
|
}
|
|
88
90
|
/** Fields every row holds as a number, that measure something and vary. */
|
|
89
91
|
function numericSeries(rows, skip, units) {
|
|
90
|
-
const keys = [...new Set(rows.flatMap((row) => Object.keys(row)))].filter((key) => key !== skip &&
|
|
92
|
+
const keys = [...new Set(rows.flatMap((row) => Object.keys(row)))].filter((key) => key !== skip &&
|
|
93
|
+
rows.every((row) => typeof row[key] === 'number' || row[key] === null) &&
|
|
94
|
+
distinct(rows, key) > 1);
|
|
91
95
|
return keys.flatMap((key) => {
|
|
92
96
|
const reading = measured(key, units);
|
|
93
97
|
return reading ? [{ key, ...reading }] : [];
|
|
@@ -105,7 +109,8 @@ function sharedWords(series) {
|
|
|
105
109
|
const words = series.map((one) => one.label.split(' '));
|
|
106
110
|
const [first = []] = words;
|
|
107
111
|
let shared = 0;
|
|
108
|
-
while (shared < first.length &&
|
|
112
|
+
while (shared < first.length &&
|
|
113
|
+
words.every((one) => one.length > shared + 1 && one[shared] === first[shared]))
|
|
109
114
|
shared++;
|
|
110
115
|
if (!shared)
|
|
111
116
|
return undefined;
|
|
@@ -131,7 +136,9 @@ function timeCharts(path, title, rows, x, units) {
|
|
|
131
136
|
return {
|
|
132
137
|
kind: 'time',
|
|
133
138
|
key: `${path}:${shown.map((series) => series.key).join('+')}`,
|
|
134
|
-
title: shown.length === 1
|
|
139
|
+
title: shown.length === 1
|
|
140
|
+
? (shown[0]?.label ?? title)
|
|
141
|
+
: (shared?.title ?? (title || shown.map((series) => series.label).join(' · '))),
|
|
135
142
|
x,
|
|
136
143
|
series: shared?.series ?? shown,
|
|
137
144
|
rows,
|
|
@@ -142,7 +149,9 @@ function timeCharts(path, title, rows, x, units) {
|
|
|
142
149
|
function labelKey(rows) {
|
|
143
150
|
const [first] = rows;
|
|
144
151
|
const title = first ? titleKey(first) : undefined;
|
|
145
|
-
if (!first ||
|
|
152
|
+
if (!first ||
|
|
153
|
+
!title ||
|
|
154
|
+
!rows.every((row) => typeof row[title] === 'string' || typeof row[title] === 'number'))
|
|
146
155
|
return undefined;
|
|
147
156
|
if (distinct(rows, title) === rows.length)
|
|
148
157
|
return title;
|
|
@@ -193,7 +202,6 @@ function chartsIn(value, path, title, units, depth) {
|
|
|
193
202
|
const entries = shownEntries(value);
|
|
194
203
|
const numbers = entries.filter(([key, entry]) => typeof entry === 'number' && isMeasure(key));
|
|
195
204
|
if (depth > 0 && numbers.length >= MIN_POINTS && numbers.length === entries.length) {
|
|
196
|
-
// An object of numbers (`rates`): each key a bar.
|
|
197
205
|
const rows = numbers.map(([key, entry]) => ({ label: keyLabel(key), value: entry }));
|
|
198
206
|
return [
|
|
199
207
|
{
|
|
@@ -247,13 +255,18 @@ export function returnedMedia(layout, parts, imageAlt) {
|
|
|
247
255
|
return src ? [{ type: part.type, src, mimeType: part.mimeType }] : [];
|
|
248
256
|
});
|
|
249
257
|
return {
|
|
250
|
-
images: [
|
|
251
|
-
|
|
258
|
+
images: [
|
|
259
|
+
...sourced.filter((part) => part.type === 'image').map(({ src }) => ({ src, alt: imageAlt })),
|
|
260
|
+
...layout.images,
|
|
261
|
+
],
|
|
262
|
+
audio: sourced
|
|
263
|
+
.filter((part) => part.type === 'audio')
|
|
264
|
+
.map(({ src, mimeType }) => ({ src, ...(mimeType ? { mimeType } : {}) })),
|
|
252
265
|
};
|
|
253
266
|
}
|
|
254
267
|
/** Whether the result has anything to lead with above its data. */
|
|
255
268
|
export function hasLead(layout, media) {
|
|
256
|
-
return layout.figures.length + layout.charts.length + media.images.length + media.audio.length > 0;
|
|
269
|
+
return (layout.figures.length + layout.charts.length + media.images.length + media.audio.length > 0);
|
|
257
270
|
}
|
|
258
271
|
const DAY_MS = 86_400_000;
|
|
259
272
|
/** Days a time chart names by weekday; longer runs read as dates. */
|
|
@@ -266,10 +279,15 @@ const MAX_WEEKDAYS = 10;
|
|
|
266
279
|
export function timeFormats(dates) {
|
|
267
280
|
if (dates.some((date) => date.includes('T'))) {
|
|
268
281
|
const span = Math.abs(Date.parse(dates.at(-1) ?? '') - Date.parse(dates[0] ?? ''));
|
|
269
|
-
return {
|
|
282
|
+
return {
|
|
283
|
+
tick: span <= DAY_MS ? { hour: 'numeric' } : { month: 'short', day: 'numeric' },
|
|
284
|
+
when: { dateStyle: 'medium', timeStyle: 'short' },
|
|
285
|
+
};
|
|
270
286
|
}
|
|
271
287
|
return {
|
|
272
|
-
tick: dates.length <= MAX_WEEKDAYS
|
|
288
|
+
tick: dates.length <= MAX_WEEKDAYS
|
|
289
|
+
? { weekday: 'short', timeZone: 'UTC' }
|
|
290
|
+
: { month: 'short', day: 'numeric', timeZone: 'UTC' },
|
|
273
291
|
when: { dateStyle: 'medium', timeZone: 'UTC' },
|
|
274
292
|
};
|
|
275
293
|
}
|
package/client/tool-resume.d.ts
CHANGED
|
@@ -7,12 +7,28 @@ export type ToolGateResolution = {
|
|
|
7
7
|
| {
|
|
8
8
|
action: 'auth';
|
|
9
9
|
secret?: string;
|
|
10
|
+
}
|
|
11
|
+
/** The page answered a `page` gate: no person decides it. */
|
|
12
|
+
| {
|
|
13
|
+
action: 'page';
|
|
14
|
+
page: PagePart;
|
|
15
|
+
};
|
|
16
|
+
/** What the page sends for a call it was asked to answer: its output, or that nothing here answers the tool. */
|
|
17
|
+
export type PagePart = {
|
|
18
|
+
output: unknown;
|
|
19
|
+
} | {
|
|
20
|
+
unanswered: true;
|
|
10
21
|
};
|
|
11
22
|
/** An answer on its way to the gate on `callId`; the gate shows it until the answer settles or fails. */
|
|
12
23
|
export type AnsweringGate = {
|
|
13
24
|
callId: string;
|
|
14
|
-
|
|
25
|
+
operationId: string;
|
|
26
|
+
action: PersonGateResolution['action'];
|
|
15
27
|
};
|
|
28
|
+
/** An answer a person gives: every resolution but the page's own. */
|
|
29
|
+
export type PersonGateResolution = Exclude<ToolGateResolution, {
|
|
30
|
+
action: 'page';
|
|
31
|
+
}>;
|
|
16
32
|
/**
|
|
17
33
|
* What the browser sends for the user's answer to a gate. The host settles a
|
|
18
34
|
* refusal; an approval carries the permissions the session holds after it,
|
|
@@ -23,6 +39,7 @@ export type GatedToolContinue = {
|
|
|
23
39
|
} | {
|
|
24
40
|
decision: 'approve';
|
|
25
41
|
secret?: string;
|
|
42
|
+
page?: PagePart;
|
|
26
43
|
sessionPermissions: string[];
|
|
27
44
|
};
|
|
28
45
|
export declare function continueGatedToolInvocation(args: {
|
package/client/tool-resume.js
CHANGED
|
@@ -5,7 +5,10 @@ export function continueGatedToolInvocation(args) {
|
|
|
5
5
|
const { resolution } = args;
|
|
6
6
|
return {
|
|
7
7
|
decision: 'approve',
|
|
8
|
-
...(resolution.action === 'auth' && resolution.secret !== undefined
|
|
8
|
+
...(resolution.action === 'auth' && resolution.secret !== undefined
|
|
9
|
+
? { secret: resolution.secret }
|
|
10
|
+
: {}),
|
|
11
|
+
...(resolution.action === 'page' ? { page: resolution.page } : {}),
|
|
9
12
|
sessionPermissions: sessionPermissionsAfterApproval(args.sessionPermissions, args.toolName, args.gate.permission),
|
|
10
13
|
};
|
|
11
14
|
}
|
package/client/trace-feed.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { TraceRecord } from '@theoremjs/agents';
|
|
2
2
|
/**
|
|
3
3
|
* Trace records a host sends back to the client, in arrival order. Only a host
|
|
4
|
-
* that delivers its traces (the
|
|
4
|
+
* that delivers its traces (the studio) has one; `useTraceRecords` reads it.
|
|
5
5
|
*/
|
|
6
6
|
export interface TraceFeed {
|
|
7
7
|
push(record: TraceRecord): void;
|
package/client/trace-story.d.ts
CHANGED
|
@@ -61,7 +61,7 @@ export type TraceStep = {
|
|
|
61
61
|
/** The last message's text from `role`, read from a span's input or output messages. */
|
|
62
62
|
export declare function messageText(node: TraceNode, key: 'gen_ai.input.messages' | 'gen_ai.output.messages', role: string): string;
|
|
63
63
|
/**
|
|
64
|
-
* A turn as it happened: what the user asked, each model call and the tool
|
|
64
|
+
* A turn as it happened: what the user asked, each model call, and the tool
|
|
65
65
|
* calls it asked for (nested under it), the events that changed the course,
|
|
66
66
|
* and how it ended. HTTP tries stay in the timeline; they are how a call
|
|
67
67
|
* travelled, not a step of the story.
|
|
@@ -71,7 +71,7 @@ export declare function traceStory(root: TraceNode): TraceStep[];
|
|
|
71
71
|
export declare function toolArguments(node: TraceNode): [string, string][];
|
|
72
72
|
/** A stored JSON or text value, parsed when it is JSON. */
|
|
73
73
|
export declare function storedValue(node: TraceNode, key: string): unknown;
|
|
74
|
-
/** How a span ended when it did not simply succeed: a tool denied, paused or failed, or any span's error. */
|
|
74
|
+
/** How a span ended when it did not simply succeed: a tool denied, paused, or failed, or any span's error. */
|
|
75
75
|
export interface TraceOutcome {
|
|
76
76
|
label: string;
|
|
77
77
|
doc?: string;
|
package/client/trace-story.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { inlineContent, traceAttributeMeta, traceEventAttributeMeta, traceEventMeta } from '@theoremjs/agents';
|
|
1
|
+
import { inlineContent, traceAttributeMeta, traceEventAttributeMeta, traceEventMeta, } from '@theoremjs/agents';
|
|
2
2
|
import { isRecord } from '@theoremjs/agents/kernel';
|
|
3
|
-
import { nanosToMs, traceSpans, traceTotals } from "./trace-view.js";
|
|
3
|
+
import { nanosToMs, traceSpans, traceTotals, } from "./trace-view.js";
|
|
4
4
|
export function traceTurns(tree) {
|
|
5
5
|
return tree.map((node) => ({
|
|
6
6
|
node,
|
|
@@ -44,7 +44,7 @@ export function traceActor(node) {
|
|
|
44
44
|
return 'theorem';
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
/** Events worth a line of their own: something acted, retried, waited or warned. */
|
|
47
|
+
/** Events worth a line of their own: something acted, retried, waited, or warned. */
|
|
48
48
|
const STORY_EVENTS = new Set([
|
|
49
49
|
'theorem.guardrail',
|
|
50
50
|
'theorem.attempt.retry',
|
|
@@ -116,7 +116,7 @@ function storyEvents(node, turnStart) {
|
|
|
116
116
|
});
|
|
117
117
|
}
|
|
118
118
|
/**
|
|
119
|
-
* A turn as it happened: what the user asked, each model call and the tool
|
|
119
|
+
* A turn as it happened: what the user asked, each model call, and the tool
|
|
120
120
|
* calls it asked for (nested under it), the events that changed the course,
|
|
121
121
|
* and how it ended. HTTP tries stay in the timeline; they are how a call
|
|
122
122
|
* travelled, not a step of the story.
|
|
@@ -185,7 +185,9 @@ export function toolArguments(node) {
|
|
|
185
185
|
}
|
|
186
186
|
if (!isRecord(value))
|
|
187
187
|
return [];
|
|
188
|
-
return Object.entries(value).flatMap(([key, item]) => typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean'
|
|
188
|
+
return Object.entries(value).flatMap(([key, item]) => typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean'
|
|
189
|
+
? [[key, String(item)]]
|
|
190
|
+
: []);
|
|
189
191
|
}
|
|
190
192
|
/** A stored JSON or text value, parsed when it is JSON. */
|
|
191
193
|
export function storedValue(node, key) {
|
|
@@ -209,14 +211,22 @@ export function traceOutcome(node) {
|
|
|
209
211
|
const codePart = typeof code === 'string' ? { code } : {};
|
|
210
212
|
const tool = outcome !== 'ok' ? optionOf('theorem.tool.outcome', outcome) : undefined;
|
|
211
213
|
if (tool)
|
|
212
|
-
return {
|
|
214
|
+
return {
|
|
215
|
+
...tool,
|
|
216
|
+
...codePart,
|
|
217
|
+
tone: status.code === 'ERROR' || outcome === 'error' ? 'error' : 'warning',
|
|
218
|
+
};
|
|
213
219
|
if (status.code !== 'ERROR')
|
|
214
220
|
return undefined;
|
|
215
221
|
const type = attributes['error.type'];
|
|
216
222
|
const kind = optionOf('error.type', type);
|
|
217
223
|
if (kind)
|
|
218
224
|
return { ...kind, ...codePart, tone: 'error' };
|
|
219
|
-
return {
|
|
225
|
+
return {
|
|
226
|
+
label: typeof type === 'string' ? type : (status.message ?? ''),
|
|
227
|
+
...codePart,
|
|
228
|
+
tone: 'error',
|
|
229
|
+
};
|
|
220
230
|
}
|
|
221
231
|
function guardrailHits(value) {
|
|
222
232
|
if (!Array.isArray(value))
|
|
@@ -226,7 +236,7 @@ function guardrailHits(value) {
|
|
|
226
236
|
if (typeof hit.rule !== 'string')
|
|
227
237
|
return [];
|
|
228
238
|
const severity = typeof hit.severity === 'string' ? hit.severity : '';
|
|
229
|
-
// A host names its own rules on the hit; Theorem's are named in the catalog.
|
|
239
|
+
// why: A host names its own rules on the hit; Theorem's are named in the catalog.
|
|
230
240
|
const rule = fields?.rule?.options?.[hit.rule];
|
|
231
241
|
const ruleDoc = typeof hit.doc === 'string' ? hit.doc : rule?.doc;
|
|
232
242
|
return [
|
|
@@ -268,7 +278,9 @@ export function traceGuardrails(root) {
|
|
|
268
278
|
action,
|
|
269
279
|
actionLabel: actionMeta?.label ?? action,
|
|
270
280
|
...(actionMeta && { actionDoc: actionMeta.doc }),
|
|
271
|
-
...(typeof attributes.duration_ms === 'number' && {
|
|
281
|
+
...(typeof attributes.duration_ms === 'number' && {
|
|
282
|
+
durationMs: attributes.duration_ms,
|
|
283
|
+
}),
|
|
272
284
|
hits: guardrailHits(attributes.hits),
|
|
273
285
|
...(typeof provenance?.tool === 'string' && { tool: provenance.tool }),
|
|
274
286
|
...(typeof reason === 'string' && reason && { reason }),
|
|
@@ -290,7 +302,9 @@ function union(intervals) {
|
|
|
290
302
|
return total;
|
|
291
303
|
}
|
|
292
304
|
function intervalsOf(nodes, types) {
|
|
293
|
-
return nodes
|
|
305
|
+
return nodes
|
|
306
|
+
.filter((node) => types.has(node.meta.type))
|
|
307
|
+
.map((node) => [node.startMs, node.startMs + node.durationMs]);
|
|
294
308
|
}
|
|
295
309
|
const MODEL_TYPES = new Set(['call', 'response', 'decision']);
|
|
296
310
|
const TOOL_TYPES = new Set(['tool']);
|
|
@@ -311,8 +325,10 @@ function eventsMs(nodes, name, key) {
|
|
|
311
325
|
* their own share, so the model and tools keep only their own work.
|
|
312
326
|
*/
|
|
313
327
|
function traceTimeSplit(root) {
|
|
314
|
-
// A host's call is its own root: the tool span is the whole trace.
|
|
315
|
-
const spans = TOOL_TYPES.has(root.meta.type)
|
|
328
|
+
// why: A host's call is its own root: the tool span is the whole trace.
|
|
329
|
+
const spans = TOOL_TYPES.has(root.meta.type)
|
|
330
|
+
? [root, ...traceSpans(root.children)]
|
|
331
|
+
: traceSpans(root.children);
|
|
316
332
|
const model = intervalsOf(spans, MODEL_TYPES);
|
|
317
333
|
const tools = intervalsOf(spans, TOOL_TYPES);
|
|
318
334
|
const toolNodes = spans.filter((node) => TOOL_TYPES.has(node.meta.type));
|
|
@@ -321,10 +337,12 @@ function traceTimeSplit(root) {
|
|
|
321
337
|
return total + (typeof value === 'number' ? value : 0);
|
|
322
338
|
}, 0);
|
|
323
339
|
const modelMs = union(model);
|
|
324
|
-
const
|
|
340
|
+
const covered = union([...model, ...tools]);
|
|
341
|
+
// why: A tool that runs an agent holds that agent's model calls: their time is the model's, not the tool's too.
|
|
342
|
+
const toolsMs = covered - modelMs;
|
|
325
343
|
const toolGuard = Math.min(toolsMs, eventsMs(toolNodes, 'theorem.guardrail', 'duration_ms'));
|
|
326
344
|
const toolHooks = Math.min(toolsMs - toolGuard, eventsMs(toolNodes, 'theorem.stage', 'hook_ms'));
|
|
327
|
-
const between = Math.max(0, root.durationMs -
|
|
345
|
+
const between = Math.max(0, root.durationMs - covered);
|
|
328
346
|
const turnGuard = Math.min(between, eventMs(root, 'theorem.guardrail', 'duration_ms'));
|
|
329
347
|
const turnHooks = Math.min(between - turnGuard, eventMs(root, 'theorem.stage', 'hook_ms'));
|
|
330
348
|
return {
|
|
@@ -344,7 +362,6 @@ export function traceLatency(root) {
|
|
|
344
362
|
const spans = traceSpans([root]);
|
|
345
363
|
const calls = spans.filter((node) => MODEL_TYPES.has(node.meta.type));
|
|
346
364
|
const firstTextMs = secondsAttribute(root, 'theorem.turn.time_to_first_text');
|
|
347
|
-
// When the model first wrote, measured from the turn's start.
|
|
348
365
|
const written = calls
|
|
349
366
|
.map((node) => {
|
|
350
367
|
const at = secondsAttribute(node, 'theorem.response.time_to_first_text');
|
|
@@ -379,14 +396,15 @@ export function traceLatency(root) {
|
|
|
379
396
|
if (typeof event.attributes.runs === 'number')
|
|
380
397
|
streamChecksMs += took;
|
|
381
398
|
}
|
|
382
|
-
// A call's stream checks carry their own time; the call's total adds only what they do not (a trace from before they did).
|
|
399
|
+
// why: A call's stream checks carry their own time; the call's total adds only what they do not (a trace from before they did).
|
|
383
400
|
const stream = node.span.attributes['theorem.guardrail.stream_ms'];
|
|
384
401
|
if (typeof stream === 'number')
|
|
385
402
|
guardMs += Math.max(0, stream - streamChecksMs);
|
|
386
403
|
}
|
|
387
404
|
return {
|
|
388
405
|
...(firstTextMs !== undefined && { firstTextMs }),
|
|
389
|
-
...(firstTextMs !== undefined &&
|
|
406
|
+
...(firstTextMs !== undefined &&
|
|
407
|
+
Number.isFinite(firstWritten) && { heldMs: Math.max(0, firstTextMs - firstWritten) }),
|
|
390
408
|
...(writingMs > 0 && { tokensPerSecond: tokens / (writingMs / MS_PER_S) }),
|
|
391
409
|
...(checks > 0 && { guardrails: { checks, ms: guardMs, flagged } }),
|
|
392
410
|
};
|
|
@@ -408,9 +426,15 @@ export function traceActivity(root, buckets) {
|
|
|
408
426
|
const model = intervalsOf(spans, MODEL_TYPES);
|
|
409
427
|
const tools = intervalsOf(spans, TOOL_TYPES);
|
|
410
428
|
const width = Math.max(root.durationMs, 1) / buckets;
|
|
411
|
-
const share = (intervals, from, to) => Math.min(1, union(intervals
|
|
429
|
+
const share = (intervals, from, to) => Math.min(1, union(intervals
|
|
430
|
+
.map(([a, b]) => [Math.max(a, from), Math.min(b, to)])
|
|
431
|
+
.filter(([a, b]) => b > a)) / width);
|
|
412
432
|
return Array.from({ length: buckets + 1 }, (_, index) => {
|
|
413
433
|
const from = root.startMs + index * width;
|
|
414
|
-
return {
|
|
434
|
+
return {
|
|
435
|
+
at: index * width,
|
|
436
|
+
model: share(model, from, from + width),
|
|
437
|
+
tools: share(tools, from, from + width),
|
|
438
|
+
};
|
|
415
439
|
});
|
|
416
440
|
}
|
package/client/trace-view.d.ts
CHANGED
|
@@ -95,9 +95,9 @@ export interface TraceFilter {
|
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
97
|
/**
|
|
98
|
-
* The fields a search over `nodes` offers: type, status, duration and events,
|
|
98
|
+
* The fields a search over `nodes` offers: type, status, duration, and events,
|
|
99
99
|
* then every attribute the spans recorded that compares as a value. Stored
|
|
100
|
-
* text, JSON and structured values are reached by the text field.
|
|
100
|
+
* text, JSON, and structured values are reached by the text field.
|
|
101
101
|
*/
|
|
102
102
|
export declare function traceSearchFields(nodes: readonly TraceNode[]): TraceSearchField[];
|
|
103
103
|
/**
|