@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.
Files changed (258) hide show
  1. package/README.md +728 -202
  2. package/client/composer-hints.js +1 -1
  3. package/client/decision-images.d.ts +76 -0
  4. package/client/decision-images.js +139 -0
  5. package/client/decision-transport.d.ts +26 -4
  6. package/client/decision-transport.js +79 -6
  7. package/client/embedding-input.d.ts +84 -0
  8. package/client/embedding-input.js +194 -0
  9. package/client/embedding-space.d.ts +82 -0
  10. package/client/embedding-space.js +389 -0
  11. package/client/embedding-transport.d.ts +40 -0
  12. package/client/embedding-transport.js +71 -0
  13. package/client/failure.d.ts +7 -10
  14. package/client/failure.js +9 -9
  15. package/client/host-call.d.ts +1 -1
  16. package/client/host-call.js +7 -2
  17. package/client/host-transport.d.ts +2 -0
  18. package/client/host-transport.js +3 -2
  19. package/client/index.d.ts +19 -11
  20. package/client/index.js +14 -10
  21. package/client/ink-waveform.d.ts +1 -1
  22. package/client/ink-waveform.js +1 -2
  23. package/client/live/apply-live-turn-tool-event.js +1 -1
  24. package/client/live/live-captions.d.ts +30 -0
  25. package/client/live/live-captions.js +105 -1
  26. package/client/live/live-mic-forward.d.ts +1 -0
  27. package/client/live/live-mic-forward.js +11 -2
  28. package/client/live/live-page-tool.d.ts +34 -0
  29. package/client/live/live-page-tool.js +11 -0
  30. package/client/live/live-state.d.ts +4 -2
  31. package/client/live/live-state.js +2 -0
  32. package/client/live/live-video.js +1 -1
  33. package/client/live/run-live-tool-call.d.ts +7 -2
  34. package/client/live/run-live-tool-call.js +48 -10
  35. package/client/live/use-live-caption-log.d.ts +17 -0
  36. package/client/live/use-live-caption-log.js +51 -0
  37. package/client/live-client.d.ts +52 -4
  38. package/client/live-client.js +250 -59
  39. package/client/live-messages.d.ts +51 -13
  40. package/client/live-messages.js +42 -9
  41. package/client/page-dispatch.d.ts +15 -0
  42. package/client/page-dispatch.js +63 -0
  43. package/client/run-commit.d.ts +16 -43
  44. package/client/run-commit.js +26 -101
  45. package/client/run-session.d.ts +40 -41
  46. package/client/run-session.js +104 -112
  47. package/client/schema-fields.js +27 -6
  48. package/client/shaped-data.js +37 -8
  49. package/client/source-citations.js +1 -1
  50. package/client/space-geometry.d.ts +39 -0
  51. package/client/space-geometry.js +183 -0
  52. package/client/state-fields.js +1 -1
  53. package/client/tool-result.js +30 -12
  54. package/client/tool-resume.d.ts +18 -1
  55. package/client/tool-resume.js +4 -1
  56. package/client/trace-feed.d.ts +1 -1
  57. package/client/trace-story.d.ts +2 -2
  58. package/client/trace-story.js +43 -19
  59. package/client/trace-view.d.ts +2 -2
  60. package/client/trace-view.js +56 -16
  61. package/client/transcript-groups.d.ts +28 -2
  62. package/client/transcript-groups.js +63 -13
  63. package/client/transcript-schema.d.ts +10 -0
  64. package/client/transcript-schema.js +120 -0
  65. package/client/transport.d.ts +114 -26
  66. package/client/transport.js +106 -32
  67. package/client/turn-client.d.ts +17 -8
  68. package/client/turn-client.js +26 -31
  69. package/client/voice-recorder.d.ts +1 -1
  70. package/client/voice-recorder.js +2 -5
  71. package/client/wire-line.js +5 -5
  72. package/client/worklets/mic-capture.js +9 -9
  73. package/components/InkWaveform.d.ts +12 -1
  74. package/components/InkWaveform.js +65 -42
  75. package/components/live/use-live-runner-controls.d.ts +3 -4
  76. package/components/live/use-live-runner-controls.js +6 -10
  77. package/components/live/use-live-runner-model.d.ts +10 -6
  78. package/components/live/use-live-runner-model.js +29 -14
  79. package/components/live/use-live-runner-ui.d.ts +10 -8
  80. package/components/live/use-live-runner-ui.js +19 -11
  81. package/components/live/use-live-session-client.d.ts +4 -4
  82. package/components/live/use-live-session-client.js +11 -8
  83. package/components/use-composer-voice.d.ts +4 -4
  84. package/components/use-composer-voice.js +8 -8
  85. package/components/use-editor-selection.js +4 -1
  86. package/components/use-voice-playback.d.ts +2 -1
  87. package/components/use-voice-playback.js +4 -4
  88. package/hooks/use-described.d.ts +5 -1
  89. package/hooks/use-described.js +11 -9
  90. package/hooks/use-embedding-index.d.ts +27 -0
  91. package/hooks/use-embedding-index.js +43 -0
  92. package/hooks/use-one-call.d.ts +31 -0
  93. package/hooks/use-one-call.js +76 -0
  94. package/hooks/use-page-tool-warnings.d.ts +6 -0
  95. package/hooks/use-page-tool-warnings.js +25 -0
  96. package/hooks/use-second-ticker.d.ts +2 -0
  97. package/hooks/use-second-ticker.js +13 -0
  98. package/hooks/use-theorem-chat-actions.d.ts +25 -21
  99. package/hooks/use-theorem-chat-actions.js +67 -45
  100. package/hooks/use-theorem-chat-state.d.ts +6 -6
  101. package/hooks/use-theorem-chat-state.js +13 -9
  102. package/hooks/use-theorem-chat.d.ts +48 -14
  103. package/hooks/use-theorem-chat.js +329 -141
  104. package/hooks/use-theorem-decision.d.ts +5 -10
  105. package/hooks/use-theorem-decision.js +4 -67
  106. package/hooks/use-theorem-embedding.d.ts +18 -0
  107. package/hooks/use-theorem-embedding.js +12 -0
  108. package/hooks/use-theorem-host.d.ts +5 -4
  109. package/hooks/use-theorem-host.js +26 -8
  110. package/hooks/use-theorem-interface.d.ts +6 -3
  111. package/hooks/use-theorem-interface.js +12 -22
  112. package/index.d.ts +6 -3
  113. package/index.js +5 -2
  114. package/live.d.ts +1 -0
  115. package/live.js +1 -1
  116. package/package.json +6 -5
  117. package/server/checkpoint-ownership.d.ts +6 -0
  118. package/server/checkpoint-ownership.js +42 -0
  119. package/server/conversation-store.d.ts +92 -0
  120. package/server/conversation-store.js +131 -0
  121. package/server/conversation.d.ts +193 -0
  122. package/server/conversation.js +552 -0
  123. package/server/decision-handler.d.ts +12 -4
  124. package/server/decision-handler.js +29 -38
  125. package/server/embedding-handler.d.ts +33 -0
  126. package/server/embedding-handler.js +45 -0
  127. package/server/handler.d.ts +58 -20
  128. package/server/handler.js +441 -238
  129. package/server/mod.d.ts +15 -6
  130. package/server/mod.js +13 -5
  131. package/server/page-dispatch.d.ts +22 -0
  132. package/server/page-dispatch.js +127 -0
  133. package/server/request-body.d.ts +2 -0
  134. package/server/request-body.js +42 -0
  135. package/server/request-check.d.ts +6 -1
  136. package/server/request-check.js +27 -12
  137. package/server/session-authority.d.ts +8 -0
  138. package/server/session-authority.js +78 -0
  139. package/server/session-boundary.d.ts +26 -0
  140. package/server/session-boundary.js +64 -0
  141. package/server/session-identity.d.ts +16 -0
  142. package/server/session-identity.js +41 -0
  143. package/server/session-limits.d.ts +12 -0
  144. package/server/session-limits.js +59 -0
  145. package/server/session-store.d.ts +69 -20
  146. package/server/session-store.js +220 -5
  147. package/server/single-action.d.ts +21 -0
  148. package/server/single-action.js +30 -0
  149. package/server/steer-inbox.js +1 -1
  150. package/server/tool-operations.d.ts +6 -0
  151. package/server/tool-operations.js +132 -0
  152. package/server/turn-input.d.ts +10 -0
  153. package/server/turn-input.js +29 -0
  154. package/styles/ink-controls.css +78 -78
  155. package/ui/CardWall.d.ts +10 -0
  156. package/ui/CardWall.js +61 -0
  157. package/ui/ChatComposerBar.d.ts +28 -5
  158. package/ui/ChatComposerBar.js +85 -30
  159. package/ui/ChatTranscript.d.ts +14 -1
  160. package/ui/ChatTranscript.js +110 -57
  161. package/ui/ConsoleFrame.d.ts +61 -4
  162. package/ui/ConsoleFrame.js +109 -9
  163. package/ui/DecisionAnswers.d.ts +40 -2
  164. package/ui/DecisionAnswers.js +166 -35
  165. package/ui/DecisionImages.d.ts +16 -0
  166. package/ui/DecisionImages.js +46 -0
  167. package/ui/EmbeddingDocuments.d.ts +78 -0
  168. package/ui/EmbeddingDocuments.js +278 -0
  169. package/ui/EmbeddingQuery.d.ts +27 -0
  170. package/ui/EmbeddingQuery.js +95 -0
  171. package/ui/EmbeddingResults.d.ts +41 -0
  172. package/ui/EmbeddingResults.js +165 -0
  173. package/ui/EmbeddingSpace.d.ts +26 -0
  174. package/ui/EmbeddingSpace.js +106 -0
  175. package/ui/LiveCaptionsPanel.d.ts +22 -0
  176. package/ui/LiveCaptionsPanel.js +27 -0
  177. package/ui/LiveRunner.d.ts +6 -4
  178. package/ui/LiveRunner.js +42 -55
  179. package/ui/PaneState.d.ts +33 -0
  180. package/ui/PaneState.js +50 -0
  181. package/ui/SchemaFields.d.ts +1 -1
  182. package/ui/SchemaFields.js +54 -26
  183. package/ui/ShapedData.js +73 -28
  184. package/ui/SidePanel.d.ts +74 -5
  185. package/ui/SidePanel.js +82 -9
  186. package/ui/StateFields.d.ts +10 -2
  187. package/ui/StateFields.js +56 -13
  188. package/ui/TheoremChat.d.ts +65 -14
  189. package/ui/TheoremChat.js +66 -78
  190. package/ui/TheoremDecision.d.ts +12 -20
  191. package/ui/TheoremDecision.js +78 -35
  192. package/ui/TheoremEmbedding.d.ts +27 -0
  193. package/ui/TheoremEmbedding.js +122 -0
  194. package/ui/TheoremHost.d.ts +22 -5
  195. package/ui/TheoremHost.js +127 -37
  196. package/ui/ToolGateCard.d.ts +5 -1
  197. package/ui/ToolGateCard.js +30 -23
  198. package/ui/ToolResult.js +12 -10
  199. package/ui/ToolResultCharts.js +29 -12
  200. package/ui/TraceCharts.d.ts +2 -2
  201. package/ui/TraceCharts.js +48 -17
  202. package/ui/TraceGuardrails.d.ts +3 -2
  203. package/ui/TraceGuardrails.js +24 -6
  204. package/ui/TraceInspector.d.ts +11 -1
  205. package/ui/TraceInspector.js +45 -28
  206. package/ui/TraceInspectorPanel.d.ts +31 -9
  207. package/ui/TraceInspectorPanel.js +77 -39
  208. package/ui/TraceOverview.d.ts +7 -3
  209. package/ui/TraceOverview.js +44 -14
  210. package/ui/TraceSpanDetail.js +24 -22
  211. package/ui/TraceStory.js +9 -8
  212. package/ui/TraceValues.js +19 -9
  213. package/ui/TraceWaterfall.js +12 -9
  214. package/ui/VoiceNote.js +4 -4
  215. package/ui/approval-heading.d.ts +4 -0
  216. package/ui/approval-heading.js +9 -0
  217. package/ui/arrive.d.ts +18 -0
  218. package/ui/arrive.js +46 -0
  219. package/ui/built/theme.css +86 -68
  220. package/ui/built/theme.css.d.ts +9 -0
  221. package/ui/built/theorem.d.ts +3 -3
  222. package/ui/built/theorem.js +34 -5
  223. package/ui/built/theorem.variants.d.ts +8 -2
  224. package/ui/code-language.d.ts +12 -0
  225. package/ui/code-language.js +50 -0
  226. package/ui/disclosure-motion.js +53 -9
  227. package/ui/embedding-space-gl.d.ts +37 -0
  228. package/ui/embedding-space-gl.js +663 -0
  229. package/ui/embedding-space-view.d.ts +8 -0
  230. package/ui/embedding-space-view.js +8 -0
  231. package/ui/glide.d.ts +7 -0
  232. package/ui/glide.js +44 -0
  233. package/ui/icons.js +2 -1
  234. package/ui/index.d.ts +20 -9
  235. package/ui/index.js +20 -10
  236. package/ui/labels-provider.js +5 -2
  237. package/ui/labels.d.ts +374 -85
  238. package/ui/labels.js +1028 -188
  239. package/ui/markdown-plugins.d.ts +9 -0
  240. package/ui/markdown-plugins.js +13 -0
  241. package/ui/mcp-icon.d.ts +6 -0
  242. package/ui/mcp-icon.js +8 -0
  243. package/ui/row-keys.d.ts +19 -0
  244. package/ui/row-keys.js +33 -0
  245. package/ui/theme.d.ts +1 -1
  246. package/ui/theme.js +2 -2
  247. package/ui/theorem-theme.js +32 -10
  248. package/ui/trace-palette.d.ts +2 -16
  249. package/ui/trace-palette.js +2 -16
  250. package/ui/trace-patterns.d.ts +11 -3
  251. package/ui/trace-patterns.js +16 -3
  252. package/ui/transcript-copy-text.js +1 -0
  253. package/ui/wall-columns.d.ts +8 -0
  254. package/ui/wall-columns.js +19 -0
  255. package/client/walk-away.d.ts +0 -34
  256. package/client/walk-away.js +0 -48
  257. package/server/walk-away.d.ts +0 -22
  258. package/server/walk-away.js +0 -41
@@ -1,22 +1,22 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Badge } from '@astryxdesign/core/Badge';
3
3
  import { EmptyState } from '@astryxdesign/core/EmptyState';
4
+ import { HStack } from '@astryxdesign/core/HStack';
4
5
  import { Icon } from '@astryxdesign/core/Icon';
5
6
  import { Item } from '@astryxdesign/core/Item';
6
7
  import { PowerSearch, } from '@astryxdesign/core/PowerSearch';
7
- import { HStack } from '@astryxdesign/core/HStack';
8
- import { ScrollableArea } from '@astryxdesign/core/ScrollableArea';
9
- import { StackItem } from '@astryxdesign/core/Stack';
10
8
  import { Text } from '@astryxdesign/core/Text';
11
9
  import { VStack } from '@astryxdesign/core/VStack';
12
- import { IconTimeline } from '@tabler/icons-react';
10
+ import { IconActivity } from '@tabler/icons-react';
13
11
  import { useMemo, useState } from 'react';
14
12
  import { isCallTrace, traceGuardrails, traceLatency, traceStory, traceTurnRow, traceTurns, } from "../client/trace-story.js";
15
13
  import { TRACE_SEARCH_OPERATORS, TRACE_TEXT_FIELD, traceMatchIds, traceSearchFields, traceSpans, traceTotals, traceTree, } from "../client/trace-view.js";
16
14
  import { useTraceRecords } from "../hooks/use-trace-records.js";
15
+ import { Stream, useArrive } from "./arrive.js";
16
+ import { PaneLayout } from "./SidePanel.js";
17
17
  import { ConversationCharts, TraceCharts } from "./TraceCharts.js";
18
18
  import { TraceGuardrails } from "./TraceGuardrails.js";
19
- import { TurnPicker, TurnStats } from "./TraceOverview.js";
19
+ import { GuardrailStats, TurnPicker, TurnStats } from "./TraceOverview.js";
20
20
  import { TraceSpanDetail } from "./TraceSpanDetail.js";
21
21
  import { ActorMark, TraceStory } from "./TraceStory.js";
22
22
  import { useTraceFormat } from "./TraceValues.js";
@@ -37,7 +37,10 @@ function unitOf(t, field) {
37
37
  function operatorValue(t, field) {
38
38
  switch (field.kind) {
39
39
  case 'options':
40
- return { type: 'enum_list', values: (field.options ?? []).map(({ value, label }) => ({ value, label })) };
40
+ return {
41
+ type: 'enum_list',
42
+ values: (field.options ?? []).map(({ value, label }) => ({ value, label })),
43
+ };
41
44
  case 'number': {
42
45
  const units = unitOf(t, field);
43
46
  return units ? { type: 'float', units } : { type: 'float' };
@@ -55,7 +58,11 @@ function searchField(t, field) {
55
58
  label: field.label,
56
59
  description: field.doc,
57
60
  ...(field.group && { group: field.group }),
58
- operators: TRACE_SEARCH_OPERATORS[field.kind].map((key) => ({ key, value, i18nKey: `@astryx.powersearch.operator.${key}` })),
61
+ operators: TRACE_SEARCH_OPERATORS[field.kind].map((key) => ({
62
+ key,
63
+ value,
64
+ i18nKey: `@astryx.powersearch.operator.${key}`,
65
+ })),
59
66
  };
60
67
  }
61
68
  /** The search over the spans shown: any text first, then the fields the spans recorded. */
@@ -97,20 +104,27 @@ function TurnRowTotals({ turn }) {
97
104
  return (_jsxs(VStack, { gap: 0, hAlign: "end", children: [_jsx(Text, { type: "supporting", hasTabularNumbers: true, children: format.duration(turn.totals.durationMs) }), turn.totals.cost ? (_jsx(Text, { type: "supporting", color: "secondary", hasTabularNumbers: true, children: format.sum(turn.totals.cost, format.usd) })) : null] }));
98
105
  }
99
106
  /** A row of the whole-trace list: a turn by what the person asked, a host's call by its tool. */
100
- function TurnRow({ turn, index, isCall, onOpen }) {
107
+ function TurnRow({ turn, index, isCall, onOpen, }) {
101
108
  const { t } = useTraceFormat();
109
+ const arrive = useArrive();
102
110
  const row = useMemo(() => traceTurnRow(turn, isCall), [turn, isCall]);
103
- return (_jsx(Item, { startContent: _jsx(ActorMark, { actor: row.actor }), label: _jsx(Text, { weight: "medium", maxLines: 1, children: row.title ?? t(isCall ? '@theorem.panel.trace.charts.hostCall' : '@theorem.panel.trace.charts.turn', { index: index + 1 }) }), description: turnRowNote(row), endContent: _jsx(TurnRowTotals, { turn: turn }), align: "start", density: "compact", onClick: onOpen }));
111
+ return (_jsx(Item, { className: arrive?.className, style: arrive?.style, startContent: _jsx(ActorMark, { actor: row.actor }), label: _jsx(Text, { weight: "medium", maxLines: 1, children: row.title ??
112
+ t(isCall ? '@theorem.panel.trace.charts.hostCall' : '@theorem.panel.trace.charts.turn', { index: index + 1 }) }), description: turnRowNote(row), endContent: _jsx(TurnRowTotals, { turn: turn }), align: "start", density: "compact", onClick: onOpen }));
104
113
  }
105
114
  /** Every turn at once: the conversation's totals, each turn's time and tokens, and the turns themselves. */
106
- function ConversationOverview({ turns, onPick }) {
115
+ function ConversationOverview({ turns, onPick, }) {
107
116
  const { t } = useTraceFormat();
108
117
  const nodes = useMemo(() => turns.map((turn) => turn.node), [turns]);
109
- // Time between turns is the person's, not the agent's: the duration sums the turns.
110
- const totals = useMemo(() => ({ ...traceTotals(nodes), durationMs: turns.reduce((total, turn) => total + turn.totals.durationMs, 0) }), [nodes, turns]);
118
+ // why: Time between turns is the person's, not the agent's: the duration sums the turns.
119
+ const totals = useMemo(() => ({
120
+ ...traceTotals(nodes),
121
+ durationMs: turns.reduce((total, turn) => total + turn.totals.durationMs, 0),
122
+ }), [nodes, turns]);
111
123
  const tools = useMemo(() => traceSpans(nodes).filter((node) => node.meta.type === 'tool').length, [nodes]);
112
124
  const isCalls = isCallTrace(turns);
113
- return (_jsxs(VStack, { gap: 5, padding: 4, children: [_jsx(Text, { weight: "semibold", children: t(isCalls ? '@theorem.panel.trace.calls.count' : '@theorem.panel.trace.conversation', { count: turns.length }) }), _jsxs(VStack, { gap: 3, children: [_jsx(TurnStats, { totals: totals, tools: tools }), _jsx(ConversationCharts, { turns: turns, onPick: onPick })] }), _jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t(isCalls ? '@theorem.panel.trace.calls' : '@theorem.panel.trace.turns') }), _jsx(VStack, { gap: 0, children: turns.map((turn, index) => (_jsx(TurnRow, { turn: turn, index: index, isCall: isCalls, onOpen: () => onPick(index) }, turn.node.id))) })] })] }));
125
+ return (_jsxs(VStack, { gap: 5, padding: 4, children: [_jsx(Text, { weight: "semibold", children: t(isCalls ? '@theorem.panel.trace.calls.count' : '@theorem.panel.trace.conversation', {
126
+ count: turns.length,
127
+ }) }), _jsxs(VStack, { gap: 3, children: [_jsx(TurnStats, { totals: totals, tools: tools }), _jsx(ConversationCharts, { turns: turns, onPick: onPick })] }), _jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t(isCalls ? '@theorem.panel.trace.calls' : '@theorem.panel.trace.turns') }), _jsx(Stream, { children: _jsx(VStack, { gap: 0, children: turns.map((turn, index) => (_jsx(TurnRow, { turn: turn, index: index, isCall: isCalls, onOpen: () => onPick(index) }, turn.node.id))) }) })] })] }));
114
128
  }
115
129
  /** One turn at a glance, then its story, then its timeline under the search. */
116
130
  function TurnOverview({ turns, index, onPick, onBack, selectedId, onSelect, }) {
@@ -126,19 +140,11 @@ function TurnOverview({ turns, index, onPick, onBack, selectedId, onSelect, }) {
126
140
  if (!turn || !root)
127
141
  return null;
128
142
  const tools = traceSpans([root]).filter((node) => node.meta.type === 'tool').length;
129
- return (_jsxs(VStack, { gap: 5, padding: 4, children: [_jsx(TurnPicker, { turns: turns, index: index, onChange: onPick, onBack: onBack }), _jsxs(VStack, { gap: 3, children: [_jsx(TurnStats, { totals: turn.totals, tools: tools, latency: latency }), _jsx(TraceCharts, { root: root, split: turn.split, onSelect: onSelect })] }), _jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t('@theorem.panel.trace.timeline') }), _jsx(PowerSearch, { config: config, filters: filters, onChange: setFilters, label: t('@theorem.panel.trace.search'), isLabelHidden: true, placeholder: t('@theorem.panel.trace.search.placeholder'), resultCount: matches?.size, size: "sm" }), _jsx(TraceWaterfall, { root: root, selectedId: selectedId, matches: matches, onSelect: onSelect })] }), story.length > 0 ? (_jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t('@theorem.panel.trace.story') }), _jsx(TraceStory, { steps: story, selectedId: selectedId, matches: matches, onSelect: onSelect })] })) : null, guardrails.length > 0 ? (_jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t('@theorem.panel.trace.guardrails') }), _jsx(TraceGuardrails, { checks: guardrails, onSelect: onSelect })] })) : null] }));
143
+ return (_jsxs(VStack, { gap: 5, padding: 4, children: [_jsx(TurnPicker, { turns: turns, index: index, onChange: onPick, onBack: onBack }), _jsxs(VStack, { gap: 3, children: [_jsx(TurnStats, { totals: turn.totals, tools: tools, latency: latency }), _jsx(TraceCharts, { root: root, split: turn.split, onSelect: onSelect })] }), _jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t('@theorem.panel.trace.timeline') }), _jsx(PowerSearch, { config: config, filters: filters, onChange: setFilters, label: t('@theorem.panel.trace.search'), isLabelHidden: true, placeholder: t('@theorem.panel.trace.search.placeholder'), resultCount: matches?.size, size: "sm" }), _jsx(TraceWaterfall, { root: root, selectedId: selectedId, matches: matches, onSelect: onSelect }, root.id)] }), story.length > 0 ? (_jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t('@theorem.panel.trace.story') }), _jsx(TraceStory, { steps: story, selectedId: selectedId, matches: matches, onSelect: onSelect })] })) : null, guardrails.length > 0 ? (_jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t('@theorem.panel.trace.guardrails') }), _jsx(TraceGuardrails, { checks: guardrails, onSelect: onSelect })] })) : null] }));
130
144
  }
131
145
  function EmptyTrace() {
132
146
  const { t } = useTraceFormat();
133
- return (_jsx(VStack, { height: "100%", vAlign: "center", padding: 4, children: _jsx(EmptyState, { icon: _jsx(Icon, { icon: IconTimeline, size: "lg", color: "secondary" }), title: t('@theorem.panel.trace.empty.title'), description: t('@theorem.panel.trace.empty.description'), isCompact: true }) }));
134
- }
135
- /** Narrow, the open span takes the overview's place; wide, it sits beside it. */
136
- function TraceLayout({ isWide, overview, detail, detailLabel }) {
137
- const { t } = useTraceFormat();
138
- if (!isWide) {
139
- return (_jsxs(ScrollableArea, { label: t('@theorem.panel.trace.name'), height: "100%", children: [_jsx(TracePatterns, {}), detail ?? overview] }));
140
- }
141
- return (_jsxs(HStack, { height: "100%", gap: 0, children: [_jsx(StackItem, { size: "fill", children: _jsxs(ScrollableArea, { label: t('@theorem.panel.trace.name'), height: "100%", children: [_jsx(TracePatterns, {}), overview] }) }), detail ? (_jsx("div", { style: { width: '48%', flexShrink: 0, height: '100%', borderInlineStart: '1px solid var(--color-border)' }, children: _jsx(ScrollableArea, { label: detailLabel, height: "100%", children: detail }) })) : null] }));
147
+ return (_jsx(VStack, { height: "100%", vAlign: "center", padding: 4, children: _jsx(EmptyState, { icon: _jsx(Icon, { icon: IconActivity, size: "lg", color: "secondary" }), title: t('@theorem.panel.trace.empty.title'), description: t('@theorem.panel.trace.empty.description'), isCompact: true }) }));
142
148
  }
143
149
  /** The open span of the open turn; opening another turn closes it. */
144
150
  function useOpenSpan(turns) {
@@ -159,7 +165,7 @@ function useOpenSpan(turns) {
159
165
  };
160
166
  }
161
167
  /** The open span beside or in place of the overview; nothing while none is open. */
162
- function SpanPane({ open, turnStartMs, isWide, onBack, onOpen }) {
168
+ function SpanPane({ open, turnStartMs, isWide, onBack, onOpen, }) {
163
169
  if (!open)
164
170
  return null;
165
171
  return (_jsx(VStack, { padding: 4, children: _jsx(TraceSpanDetail, { node: open, turnStartMs: turnStartMs, isClose: isWide, onBack: onBack, onOpen: onOpen }) }));
@@ -174,16 +180,27 @@ function Overview({ turns, index, pick, selectedId, select, }) {
174
180
  * The panel's content. Narrow, the turn fills it and a span opens in its
175
181
  * place; wide, an open span sits beside the turn.
176
182
  */
177
- function TraceInspectorBody({ records, isWide }) {
183
+ function TraceInspectorBody({ records, isWide, }) {
178
184
  const tree = useMemo(() => traceTree(records), [records]);
179
185
  const turns = useMemo(() => traceTurns(tree), [tree]);
186
+ const { t } = useTraceFormat();
180
187
  const { index, root, open, select, close, pick } = useOpenSpan(turns);
181
188
  if (turns.length === 0)
182
189
  return _jsx(EmptyTrace, {});
183
- const overview = _jsx(Overview, { turns: turns, index: index, pick: pick, selectedId: open?.id, select: select });
184
- return (_jsx(TraceLayout, { isWide: isWide, overview: overview, detail: open && _jsx(SpanPane, { open: open, turnStartMs: root?.startMs ?? 0, isWide: isWide, onBack: close, onOpen: select }), detailLabel: open?.meta.label ?? '' }));
190
+ const overview = (_jsx(Overview, { turns: turns, index: index, pick: pick, selectedId: open?.id, select: select }));
191
+ return (_jsx(PaneLayout, { isWide: isWide, label: t('@theorem.panel.trace.name'), detailLabel: open?.meta.label ?? '', lead: _jsx(TracePatterns, {}), overview: overview, detail: open && (_jsx(SpanPane, { open: open, turnStartMs: root?.startMs ?? 0, isWide: isWide, onBack: close, onOpen: select })) }));
185
192
  }
186
193
  /** The inspector over the records a feed has delivered; the panel loads this module when it first opens. */
187
- export function TraceFeedBody({ traces, isWide }) {
194
+ export function TraceFeedBody({ traces, isWide, }) {
188
195
  return _jsx(TraceInspectorBody, { records: useTraceRecords(traces), isWide: isWide });
189
196
  }
197
+ /**
198
+ * Only what the records say of guardrails: how long the checks took, how many
199
+ * ran and acted, then each check. `head` leads it.
200
+ */
201
+ export function TraceGuardrailsBody({ records, head, }) {
202
+ const { t } = useTraceFormat();
203
+ const roots = useMemo(() => traceTurns(traceTree(records)).map((turn) => turn.node), [records]);
204
+ const checks = useMemo(() => roots.flatMap(traceGuardrails), [roots]);
205
+ return (_jsxs(VStack, { gap: 5, padding: 4, children: [head, roots.map((root) => (_jsx(GuardrailStats, { latency: traceLatency(root) }, root.id))), checks.length > 0 ? (_jsxs(VStack, { gap: 2, children: [_jsx(SectionTitle, { title: t('@theorem.panel.trace.guardrails') }), _jsx(TraceGuardrails, { checks: checks })] })) : null] }));
206
+ }
@@ -1,24 +1,46 @@
1
- import { type ReactNode } from 'react';
1
+ import type { TraceRecord } from '@theoremjs/agents';
2
2
  import type { ProfileObservabilityView } from '@theoremjs/agents/interface';
3
+ import { type ReactNode } from 'react';
3
4
  import type { TraceFeed } from '../client/trace-feed.ts';
5
+ import { type SidePanelLabels } from './SidePanel.tsx';
4
6
  export type TraceInspector = {
5
7
  /** The header toggle; null when the host drives the trace or the profile records none. */
6
8
  toggle: ReactNode;
7
- /** The trace, full size, while it's open; null otherwise. */
9
+ /** The trace, while it's open; null otherwise. */
8
10
  view: ReactNode;
11
+ /** Words for the docked panel. */
12
+ labels: SidePanelLabels;
13
+ /** The profile records traces, so a panel page can dock them even when the host drives the toggle. */
14
+ offered: boolean;
9
15
  };
16
+ /** Where an open trace sits on this page. */
17
+ export declare function TracePlacement({ value, children, }: {
18
+ value: 'replace' | 'panel';
19
+ children: ReactNode;
20
+ }): import("react").JSX.Element;
10
21
  /**
11
- * The trace inspector, offered when the profile records traces. Open, the
12
- * trace takes the surface's place; the two never share the screen. It reads
13
- * the records the host delivers on `traces`; a host that delivers none shows
14
- * the empty state. Pass `open` to drive it from the host's own control, which
15
- * hides the built-in toggle.
22
+ * The trace inspector, offered when the profile records traces. By default the
23
+ * open trace takes the surface's place. Inside `TracePlacement value="panel"`
24
+ * it docks at the inline end instead, and the theme eases that panel's width.
25
+ * It reads the records the host delivers on `traces`; a host that delivers none
26
+ * shows the empty state. Pass `open` to drive it from the host's own control,
27
+ * which hides the built-in toggle. The open trace takes the surface's place,
28
+ * unless this page is inside `TracePlacement value="panel"`, where it docks.
16
29
  */
17
30
  export declare function useTraceInspector(iface: {
18
31
  observability?: Pick<ProfileObservabilityView, 'record'>;
19
32
  }, traces?: TraceFeed, open?: boolean): TraceInspector;
20
- /** The surface, or the trace in its place; the surface stays mounted underneath, so nothing it holds is lost. */
21
- export declare function WithTrace({ inspector, children }: {
33
+ /** The surface, or the trace in its place — or, on a docked page, beside it. */
34
+ export declare function WithTrace({ inspector, children, }: {
22
35
  inspector: TraceInspector;
23
36
  children: ReactNode;
24
37
  }): import("react").JSX.Element;
38
+ /**
39
+ * What `records` say of guardrails, for a host that holds a trace of its own
40
+ * (the studio's guardrail test): the checks' time and count, then each
41
+ * check, laid out as the trace lays them out. `head` leads it.
42
+ */
43
+ export declare function TraceGuardrailsView({ records, head, }: {
44
+ records: readonly TraceRecord[];
45
+ head?: ReactNode;
46
+ }): import("react").JSX.Element;
@@ -1,57 +1,95 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Card } from '@astryxdesign/core/Card';
1
+ import { jsx as _jsx } from "react/jsx-runtime";
3
2
  import { Icon } from '@astryxdesign/core/Icon';
4
- import { Layout, LayoutContent } from '@astryxdesign/core/Layout';
5
- import { VStack } from '@astryxdesign/core/VStack';
6
- import { IconTimeline } from '@tabler/icons-react';
7
- import { lazy, Suspense, useCallback, useId, useState } from 'react';
8
- import { useLabels } from "./labels-provider.js";
9
- import { isSidePanelWide, RAISED, SidePanelHeader, SidePanelToggle } from "./SidePanel.js";
10
- // The inspector and its charts are fetched when the trace first opens, not with the chat.
3
+ import { IconActivity } from '@tabler/icons-react';
4
+ import { cloneElement, createContext, isValidElement, lazy, Suspense, use, useCallback, useId, useRef, useState, } from 'react';
5
+ import { TheoremLabelsProvider, useLabels } from "./labels-provider.js";
6
+ import { InPlace, isSidePanelWide, RaisedPane, SidePanel, SidePanelHeader, SidePanelToggle, useSidePanel, useWidth, } from "./SidePanel.js";
7
+ // why: The inspector and its charts are fetched when the trace first opens, not with the chat.
8
+ const TraceGuardrailsBody = lazy(() => import("./TraceInspector.js").then((module) => ({ default: module.TraceGuardrailsBody })));
11
9
  const TraceFeedBody = lazy(() => import("./TraceInspector.js").then((module) => ({ default: module.TraceFeedBody })));
12
- /** The element's width, kept current. */
13
- function useWidth() {
14
- const [width, setWidth] = useState(0);
15
- const ref = useCallback((node) => {
16
- if (!node)
17
- return;
18
- const observer = new ResizeObserver(([entry]) => setWidth(entry?.contentRect.width ?? 0));
19
- observer.observe(node);
20
- return () => observer.disconnect();
21
- }, []);
22
- return { ref, width };
10
+ /** `replace` takes the surface's place. `panel` docks at the inline end; its width eases with the theme. */
11
+ const TracePlacementContext = createContext('replace');
12
+ /** Where an open trace sits on this page. */
13
+ export function TracePlacement({ value, children, }) {
14
+ return _jsx(TracePlacementContext, { value: value, children: children });
23
15
  }
24
- function TraceView({ id, header, traces }) {
25
- const { ref, width } = useWidth();
26
- return (_jsx(Layout, { height: "fill", header: header, content: _jsx(LayoutContent, { padding: 0, isScrollable: false, children: _jsx(VStack, { height: "100%", paddingInline: 3, children: _jsx(Card, { variant: "transparent", height: "100%", padding: 0, style: RAISED, children: _jsx("div", { id: id, ref: ref, style: { height: '100%' }, children: _jsx(Suspense, { fallback: null, children: _jsx(TraceFeedBody, { traces: traces, isWide: isSidePanelWide(width) }) }) }) }) }) }) }));
16
+ function TraceView({ id, header, traces, }) {
17
+ return (_jsx(RaisedPane, { id: id, header: header, children: (isWide) => (_jsx(Suspense, { fallback: null, children: _jsx(TraceFeedBody, { traces: traces, isWide: isWide }) })) }));
27
18
  }
28
19
  /**
29
- * The trace inspector, offered when the profile records traces. Open, the
30
- * trace takes the surface's place; the two never share the screen. It reads
31
- * the records the host delivers on `traces`; a host that delivers none shows
32
- * the empty state. Pass `open` to drive it from the host's own control, which
33
- * hides the built-in toggle.
20
+ * The trace inspector, offered when the profile records traces. By default the
21
+ * open trace takes the surface's place. Inside `TracePlacement value="panel"`
22
+ * it docks at the inline end instead, and the theme eases that panel's width.
23
+ * It reads the records the host delivers on `traces`; a host that delivers none
24
+ * shows the empty state. Pass `open` to drive it from the host's own control,
25
+ * which hides the built-in toggle. The open trace takes the surface's place,
26
+ * unless this page is inside `TracePlacement value="panel"`, where it docks.
34
27
  */
35
28
  export function useTraceInspector(iface, traces, open) {
36
29
  const t = useLabels();
37
30
  const id = useId();
38
31
  const [own, setOwn] = useState(false);
39
- if (iface.observability?.record !== true)
40
- return { toggle: null, view: null };
41
- const isOpen = open ?? own;
32
+ const placement = use(TracePlacementContext);
42
33
  const labels = {
43
34
  name: t('@theorem.panel.trace.name'),
44
35
  show: t('@theorem.panel.trace.show'),
45
36
  hide: t('@theorem.panel.trace.hide'),
46
37
  resize: t('@theorem.panel.trace.resize'),
47
38
  };
48
- const toggle = open === undefined ? (_jsx(SidePanelToggle, { labels: labels, icon: _jsx(Icon, { icon: IconTimeline }), panelId: id, open: isOpen, onToggle: () => setOwn((value) => !value) })) : null;
49
- return {
50
- toggle,
51
- view: isOpen ? _jsx(TraceView, { id: id, header: toggle ? _jsx(SidePanelHeader, { children: toggle }) : undefined, traces: traces }) : null,
52
- };
39
+ if (iface.observability?.record !== true)
40
+ return { toggle: null, view: null, labels, offered: false };
41
+ const isOpen = open ?? own;
42
+ const toggle = open === undefined ? (_jsx(SidePanelToggle, { labels: labels, icon: _jsx(Icon, { icon: IconActivity }), panelId: id, open: isOpen, onToggle: () => setOwn((value) => !value) })) : null;
43
+ const view = isOpen ? openTrace(placement === 'panel', id, traces, toggle) : null;
44
+ return { toggle, view, labels, offered: true };
45
+ }
46
+ /** The open trace: filling the host's docked panel, or as its own panel with the toggle on top. */
47
+ function openTrace(docked, id, traces, toggle) {
48
+ if (docked)
49
+ return _jsx(TraceFill, { id: id, traces: traces });
50
+ return (_jsx(TraceView, { id: id, header: toggle ? _jsx(SidePanelHeader, { floating: true, children: toggle }) : undefined, traces: traces }));
53
51
  }
54
- /** The surface, or the trace in its place; the surface stays mounted underneath, so nothing it holds is lost. */
55
- export function WithTrace({ inspector, children }) {
56
- return (_jsxs(_Fragment, { children: [inspector.view, _jsx("div", { style: { display: inspector.view ? 'none' : 'contents' }, children: children })] }));
52
+ /** The trace body, filling the docked panel. The panel supplies the raised card. */
53
+ function TraceFill({ id, traces }) {
54
+ const { ref, width } = useWidth();
55
+ return (_jsx("div", { id: id, ref: ref, style: { height: '100%' }, children: _jsx(Suspense, { fallback: null, children: _jsx(TraceFeedBody, { traces: traces, isWide: isSidePanelWide(width) }) }) }));
56
+ }
57
+ /** The surface, with the trace docked at its inline end. A host-driven trace stays in place. */
58
+ function TraceDock({ inspector, children }) {
59
+ const layoutRef = useRef(null);
60
+ const side = useSidePanel(layoutRef, false);
61
+ const childRef = useRef(undefined);
62
+ const setLayout = useCallback((node) => {
63
+ layoutRef.current = node;
64
+ const ref = childRef.current;
65
+ if (typeof ref === 'function')
66
+ ref(node);
67
+ else if (ref != null)
68
+ ref.current = node;
69
+ }, []);
70
+ if (!isValidElement(children)) {
71
+ return _jsx(InPlace, { view: inspector.view, children: children });
72
+ }
73
+ childRef.current = children.props.ref;
74
+ const open = inspector.view != null;
75
+ return cloneElement(children, {
76
+ ref: setLayout,
77
+ end: (_jsx(SidePanel, { labels: inspector.labels, resizable: side.resizable, open: open, children: inspector.view })),
78
+ });
79
+ }
80
+ /** The surface, or the trace in its place — or, on a docked page, beside it. */
81
+ export function WithTrace({ inspector, children, }) {
82
+ const placement = use(TracePlacementContext);
83
+ if (placement === 'panel' && inspector.offered) {
84
+ return _jsx(TraceDock, { inspector: inspector, children: children });
85
+ }
86
+ return _jsx(InPlace, { view: inspector.view, children: children });
87
+ }
88
+ /**
89
+ * What `records` say of guardrails, for a host that holds a trace of its own
90
+ * (the studio's guardrail test): the checks' time and count, then each
91
+ * check, laid out as the trace lays them out. `head` leads it.
92
+ */
93
+ export function TraceGuardrailsView({ records, head, }) {
94
+ return (_jsx(TheoremLabelsProvider, { children: _jsx(Suspense, { fallback: null, children: _jsx(TraceGuardrailsBody, { records: records, head: head }) }) }));
57
95
  }
@@ -1,4 +1,4 @@
1
- import type { ReactNode } from 'react';
1
+ import { type ReactNode } from 'react';
2
2
  import { type TraceLatency, type TraceTurn } from '../client/trace-story.ts';
3
3
  import type { TraceTotals } from '../client/trace-view.ts';
4
4
  /** Width a chart draws at before it has measured its column: the panel's narrowest. */
@@ -23,11 +23,15 @@ export declare function TurnPicker({ turns, index, onChange, onBack, }: {
23
23
  /** Leaves the turn for the whole conversation; absent when there is only the one turn. */
24
24
  onBack?: (() => void) | undefined;
25
25
  }): import("react").JSX.Element | null;
26
+ /** A turn's guardrail figures alone, as {@link TurnStats} lays them out. */
27
+ export declare function GuardrailStats({ latency }: {
28
+ latency: TraceLatency;
29
+ }): import("react").JSX.Element | null;
26
30
  /**
27
- * Duration, first text, cost, tokens, steps and guardrails of the open turn;
31
+ * Duration, first text, cost, tokens, steps, and guardrails of the open turn;
28
32
  * a total the trace did not record is left out.
29
33
  */
30
- export declare function TurnStats({ totals, tools, latency }: {
34
+ export declare function TurnStats({ totals, tools, latency, }: {
31
35
  totals: TraceTotals;
32
36
  tools: number;
33
37
  latency?: TraceLatency;
@@ -9,9 +9,10 @@ import { Text } from '@astryxdesign/core/Text';
9
9
  import { VStack } from '@astryxdesign/core/VStack';
10
10
  import { IconArrowLeft, IconChevronLeft, IconChevronRight } from '@tabler/icons-react';
11
11
  import { TRACE_FIELDS, traceAttributeMeta } from '@theoremjs/agents';
12
+ import { useEffect, useRef } from 'react';
12
13
  import { isCallTrace } from "../client/trace-story.js";
13
- import { TRACE_HUE } from "./trace-palette.js";
14
14
  import { useTraceFormat } from "./TraceValues.js";
15
+ import { TRACE_HUE } from "./trace-palette.js";
15
16
  /** Width a chart draws at before it has measured its column: the panel's narrowest. */
16
17
  export const FIRST_WIDTH_PX = 280;
17
18
  /**
@@ -20,7 +21,15 @@ export const FIRST_WIDTH_PX = 280;
20
21
  * where it was. Clicks and pointer drags still reach the chart.
21
22
  */
22
23
  export function ChartFrame({ children }) {
23
- return _jsx("div", { onMouseDown: (event) => event.preventDefault(), children: children });
24
+ const frame = useRef(null);
25
+ useEffect(() => {
26
+ const node = frame.current;
27
+ // why: A native listener keeps the frame a plain element; it is not a control, so it takes no role.
28
+ const keepFocus = (event) => event.preventDefault();
29
+ node?.addEventListener('mousedown', keepFocus);
30
+ return () => node?.removeEventListener('mousedown', keepFocus);
31
+ }, []);
32
+ return _jsx("div", { ref: frame, children: children });
24
33
  }
25
34
  /** A chart's hover card, drawn like Astryx's own tooltip. */
26
35
  export function ChartTip({ children }) {
@@ -40,9 +49,13 @@ export function TurnPicker({ turns, index, onChange, onBack, }) {
40
49
  if (!turn)
41
50
  return null;
42
51
  const isCalls = isCallTrace(turns);
43
- return (_jsxs(VStack, { gap: 1, hAlign: "start", children: [onBack ? (_jsx(Button, { label: t(isCalls ? '@theorem.panel.trace.calls.back' : '@theorem.panel.trace.conversation.back'), variant: "ghost", size: "sm", icon: _jsx(Icon, { icon: IconArrowLeft }), onClick: onBack })) : null, _jsxs(HStack, { gap: 1, align: "center", justify: "between", width: "100%", children: [_jsxs(HStack, { gap: 2, align: "center", children: [_jsx(Text, { weight: "semibold", children: t('@theorem.panel.trace.turn', { label: turn.node.meta.label, index: index + 1, count: turns.length }) }), turn.node.meta.subject ? (_jsx(Text, { color: "secondary", maxLines: 1, children: turn.node.meta.subject })) : null] }), _jsxs(HStack, { gap: 0, children: [_jsx(IconButton, { label: t(isCalls ? '@theorem.panel.trace.calls.previous' : '@theorem.panel.trace.previous'), icon: _jsx(Icon, { icon: IconChevronLeft }), variant: "ghost", size: "sm", isDisabled: index === 0, onClick: () => onChange(index - 1) }), _jsx(IconButton, { label: t(isCalls ? '@theorem.panel.trace.calls.next' : '@theorem.panel.trace.next'), icon: _jsx(Icon, { icon: IconChevronRight }), variant: "ghost", size: "sm", isDisabled: index === turns.length - 1, onClick: () => onChange(index + 1) })] })] })] }));
52
+ return (_jsxs(VStack, { gap: 1, hAlign: "start", children: [onBack ? (_jsx(Button, { label: t(isCalls ? '@theorem.panel.trace.calls.back' : '@theorem.panel.trace.conversation.back'), variant: "ghost", size: "sm", icon: _jsx(Icon, { icon: IconArrowLeft }), onClick: onBack })) : null, _jsxs(HStack, { gap: 1, align: "center", justify: "between", width: "100%", children: [_jsxs(HStack, { gap: 2, align: "center", children: [_jsx(Text, { weight: "semibold", children: t('@theorem.panel.trace.turn', {
53
+ label: turn.node.meta.label,
54
+ index: index + 1,
55
+ count: turns.length,
56
+ }) }), turn.node.meta.subject ? (_jsx(Text, { color: "secondary", maxLines: 1, children: turn.node.meta.subject })) : null] }), _jsxs(HStack, { gap: 0, children: [_jsx(IconButton, { label: t(isCalls ? '@theorem.panel.trace.calls.previous' : '@theorem.panel.trace.previous'), icon: _jsx(Icon, { icon: IconChevronLeft }), variant: "ghost", size: "sm", isDisabled: index === 0, onClick: () => onChange(index - 1) }), _jsx(IconButton, { label: t(isCalls ? '@theorem.panel.trace.calls.next' : '@theorem.panel.trace.next'), icon: _jsx(Icon, { icon: IconChevronRight }), variant: "ghost", size: "sm", isDisabled: index === turns.length - 1, onClick: () => onChange(index + 1) })] })] })] }));
44
57
  }
45
- function Stat({ label, value, detail }) {
58
+ function Stat({ label, value, detail, }) {
46
59
  return (_jsx(Card, { padding: 3, variant: "muted", style: { background: 'var(--color-background-surface)' }, children: _jsxs(VStack, { gap: 1, children: [_jsx(Text, { type: "supporting", color: "secondary", children: label }), _jsx(Text, { type: "large", weight: "semibold", hasTabularNumbers: true, children: value }), _jsx(VStack, { gap: 0, children: [detail]
47
60
  .flat()
48
61
  .filter(Boolean)
@@ -53,14 +66,18 @@ function FirstTextStat({ latency, format }) {
53
66
  return null;
54
67
  const { t } = format;
55
68
  return (_jsx(Stat, { label: t('@theorem.panel.trace.firstText'), value: format.duration(latency.firstTextMs), detail: [
56
- latency.tokensPerSecond !== undefined && t('@theorem.panel.trace.rate', { rate: format.number(Math.round(latency.tokensPerSecond)) }),
57
- latency.heldMs && t('@theorem.panel.trace.held', { duration: format.duration(latency.heldMs) }),
69
+ latency.tokensPerSecond !== undefined &&
70
+ t('@theorem.panel.trace.rate', {
71
+ rate: format.number(Math.round(latency.tokensPerSecond)),
72
+ }),
73
+ latency.heldMs &&
74
+ t('@theorem.panel.trace.held', { duration: format.duration(latency.heldMs) }),
58
75
  ] }));
59
76
  }
60
77
  function CostStat({ totals, format }) {
61
78
  if (!totals.cost)
62
79
  return null;
63
- return _jsx(Stat, { label: traceAttributeMeta('theorem.usage.cost_usd')?.label ?? '', value: format.sum(totals.cost, format.usd) });
80
+ return (_jsx(Stat, { label: traceAttributeMeta('theorem.usage.cost_usd')?.label ?? '', value: format.sum(totals.cost, format.usd) }));
64
81
  }
65
82
  /** A token total as its card reads it: "–" when no call reported it. */
66
83
  function tokenSide(total, format) {
@@ -71,26 +88,39 @@ function TokensStat({ totals, format }) {
71
88
  if (!input && !output)
72
89
  return null;
73
90
  const all = [input, output].reduce((sum, total) => sum + (total?.value ?? 0), 0);
74
- return (_jsx(Stat, { label: format.t('@theorem.panel.trace.tokens'), value: format.number(all), detail: format.t('@theorem.panel.trace.tokens.detail', { input: tokenSide(input, format), output: tokenSide(output, format) }) }));
91
+ return (_jsx(Stat, { label: format.t('@theorem.panel.trace.tokens'), value: format.number(all), detail: format.t('@theorem.panel.trace.tokens.detail', {
92
+ input: tokenSide(input, format),
93
+ output: tokenSide(output, format),
94
+ }) }));
75
95
  }
76
- function StepsStat({ totals, tools, format }) {
96
+ function StepsStat({ totals, tools, format, }) {
77
97
  const { t } = format;
78
- // With no model, every step is a tool call the heading already counts.
98
+ // why: With no model, every step is a tool call the heading already counts.
79
99
  if (totals.calls === 0 && totals.errors === 0)
80
100
  return null;
81
101
  return (_jsx(Stat, { label: t('@theorem.panel.trace.steps'), value: format.number(totals.calls + tools), detail: totals.errors > 0 ? (_jsx("span", { style: { color: TRACE_HUE.oxblood }, children: t('@theorem.panel.trace.failed', { count: totals.errors }) })) : (t('@theorem.panel.trace.steps.detail', { calls: totals.calls, tools })) }));
82
102
  }
83
- function GuardrailsStat({ latency, format }) {
103
+ function GuardrailsStat({ latency, format, }) {
84
104
  const guardrails = latency?.guardrails;
85
105
  if (!guardrails)
86
106
  return null;
87
- return (_jsx(Stat, { label: format.t('@theorem.panel.trace.guardrails'), value: format.duration(guardrails.ms), detail: format.t('@theorem.panel.trace.guardrails.detail', { checks: guardrails.checks, flagged: guardrails.flagged }) }));
107
+ return (_jsx(Stat, { label: format.t('@theorem.panel.trace.guardrails'), value: format.duration(guardrails.ms), detail: format.t('@theorem.panel.trace.guardrails.detail', {
108
+ checks: guardrails.checks,
109
+ flagged: guardrails.flagged,
110
+ }) }));
111
+ }
112
+ /** A turn's guardrail figures alone, as {@link TurnStats} lays them out. */
113
+ export function GuardrailStats({ latency }) {
114
+ const format = useTraceFormat();
115
+ if (!latency.guardrails)
116
+ return null;
117
+ return (_jsx(Grid, { columns: { minWidth: 120, repeat: 'fit' }, gap: 2, children: _jsx(GuardrailsStat, { latency: latency, format: format }) }));
88
118
  }
89
119
  /**
90
- * Duration, first text, cost, tokens, steps and guardrails of the open turn;
120
+ * Duration, first text, cost, tokens, steps, and guardrails of the open turn;
91
121
  * a total the trace did not record is left out.
92
122
  */
93
- export function TurnStats({ totals, tools, latency }) {
123
+ export function TurnStats({ totals, tools, latency, }) {
94
124
  const format = useTraceFormat();
95
125
  return (_jsxs(Grid, { columns: { minWidth: 120, repeat: 'fit' }, gap: 2, children: [_jsx(Stat, { label: TRACE_FIELDS.duration.label, value: format.duration(totals.durationMs) }), _jsx(FirstTextStat, { latency: latency, format: format }), _jsx(CostStat, { totals: totals, format: format }), _jsx(TokensStat, { totals: totals, format: format }), _jsx(StepsStat, { totals: totals, tools: tools, format: format }), _jsx(GuardrailsStat, { latency: latency, format: format })] }));
96
126
  }