@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
@@ -33,7 +33,9 @@ export function traceTree(records) {
33
33
  }
34
34
  const roots = [];
35
35
  for (const node of nodes.values()) {
36
- const parent = node.span.parentSpanId ? nodes.get(spanId(node.span.traceId, node.span.parentSpanId)) : undefined;
36
+ const parent = node.span.parentSpanId
37
+ ? nodes.get(spanId(node.span.traceId, node.span.parentSpanId))
38
+ : undefined;
37
39
  (parent ? parent.children : roots).push(node);
38
40
  }
39
41
  for (const node of nodes.values())
@@ -69,7 +71,9 @@ function tokenSum(calls, key, side) {
69
71
  }
70
72
  export function traceTotals(nodes) {
71
73
  const spans = traceSpans(nodes);
72
- const calls = spans.filter((node) => MODEL_CALL_TYPES.has(node.meta.type)).map((node) => node.span);
74
+ const calls = spans
75
+ .filter((node) => MODEL_CALL_TYPES.has(node.meta.type))
76
+ .map((node) => node.span);
73
77
  const start = Math.min(...spans.map((node) => node.startMs));
74
78
  const end = Math.max(...spans.map((node) => node.startMs + node.durationMs));
75
79
  const input = tokenSum(calls, 'gen_ai.usage.input_tokens', 'input');
@@ -101,7 +105,13 @@ const TYPE_FIELD = 'type';
101
105
  const STATUS_FIELD = 'status';
102
106
  const DURATION_FIELD = 'duration';
103
107
  const EVENTS_FIELD = 'events';
104
- const NUMBER_FORMATS = new Set(['number', 'tokens', 'seconds', 'milliseconds', 'usd']);
108
+ const NUMBER_FORMATS = new Set([
109
+ 'number',
110
+ 'tokens',
111
+ 'seconds',
112
+ 'milliseconds',
113
+ 'usd',
114
+ ]);
105
115
  const TEXT_FORMATS = new Set(['text', 'id', 'list']);
106
116
  function optionsOf(seen, known) {
107
117
  return [...seen].sort().map((value) => {
@@ -125,9 +135,9 @@ function attributeKind(meta, value) {
125
135
  return undefined;
126
136
  }
127
137
  /**
128
- * The fields a search over `nodes` offers: type, status, duration and events,
138
+ * The fields a search over `nodes` offers: type, status, duration, and events,
129
139
  * then every attribute the spans recorded that compares as a value. Stored
130
- * text, JSON and structured values are reached by the text field.
140
+ * text, JSON, and structured values are reached by the text field.
131
141
  */
132
142
  export function traceSearchFields(nodes) {
133
143
  const spans = traceSpans(nodes);
@@ -153,12 +163,27 @@ export function traceSearchFields(nodes) {
153
163
  return meta ? [[name, meta]] : [];
154
164
  }));
155
165
  const fields = [
156
- { key: TYPE_FIELD, ...TRACE_FIELDS.type, kind: 'options', options: optionsOf(types, TRACE_SPAN_TYPES) },
157
- { key: STATUS_FIELD, ...TRACE_FIELDS.status, kind: 'options', options: optionsOf(statuses, TRACE_STATUS) },
166
+ {
167
+ key: TYPE_FIELD,
168
+ ...TRACE_FIELDS.type,
169
+ kind: 'options',
170
+ options: optionsOf(types, TRACE_SPAN_TYPES),
171
+ },
172
+ {
173
+ key: STATUS_FIELD,
174
+ ...TRACE_FIELDS.status,
175
+ kind: 'options',
176
+ options: optionsOf(statuses, TRACE_STATUS),
177
+ },
158
178
  { key: DURATION_FIELD, ...TRACE_FIELDS.duration, kind: 'number', format: 'milliseconds' },
159
179
  ];
160
180
  if (events.size > 0)
161
- fields.push({ key: EVENTS_FIELD, ...TRACE_FIELDS.events, kind: 'options', options: optionsOf(events, eventLabels) });
181
+ fields.push({
182
+ key: EVENTS_FIELD,
183
+ ...TRACE_FIELDS.events,
184
+ kind: 'options',
185
+ options: optionsOf(events, eventLabels),
186
+ });
162
187
  for (const [key, { kind, seen }] of [...attributes].sort(([a], [b]) => a.localeCompare(b))) {
163
188
  const meta = traceAttributeMeta(key);
164
189
  fields.push({
@@ -172,9 +197,14 @@ export function traceSearchFields(nodes) {
172
197
  }
173
198
  return fields;
174
199
  }
175
- /** Every string a span holds: what it is, its name and status, and its attributes and events with stored text inlined. */
200
+ /** Every string a span holds: what it is, its name, and status, and its attributes and events with stored text inlined. */
176
201
  function spanText(node) {
177
- const strings = [node.meta.label, node.meta.subject ?? '', node.span.name, node.span.status.message ?? ''];
202
+ const strings = [
203
+ node.meta.label,
204
+ node.meta.subject ?? '',
205
+ node.span.name,
206
+ node.span.status.message ?? '',
207
+ ];
178
208
  const collect = (value) => {
179
209
  if (typeof value === 'string')
180
210
  strings.push(value);
@@ -225,12 +255,13 @@ function matchesFilter(node, filter) {
225
255
  case 'isNoneOf':
226
256
  return !values.some((item) => typeof item === 'string' && stringList(value).includes(item));
227
257
  case 'greaterThan':
228
- return typeof value === 'number' && values.some((item) => typeof item === 'number' && item > value);
258
+ return (typeof value === 'number' && values.some((item) => typeof item === 'number' && item > value));
229
259
  case 'lessThan':
230
- return typeof value === 'number' && values.some((item) => typeof item === 'number' && item < value);
260
+ return (typeof value === 'number' && values.some((item) => typeof item === 'number' && item < value));
231
261
  case 'contains':
232
262
  case 'notContains': {
233
- const found = typeof value === 'string' && values.some((item) => String(item).toLocaleLowerCase().includes(value.toLocaleLowerCase()));
263
+ const found = typeof value === 'string' &&
264
+ values.some((item) => String(item).toLocaleLowerCase().includes(value.toLocaleLowerCase()));
234
265
  return filter.operator === 'contains' ? found : !found;
235
266
  }
236
267
  case 'isTrue':
@@ -266,10 +297,17 @@ export function filterTraceTree(nodes, filters) {
266
297
  export function traceMatchIds(nodes, filters) {
267
298
  if (filters.length === 0)
268
299
  return undefined;
269
- return new Set(traceSpans(nodes).filter((node) => traceMatches(node, filters)).map((node) => node.id));
300
+ return new Set(traceSpans(nodes)
301
+ .filter((node) => traceMatches(node, filters))
302
+ .map((node) => node.id));
270
303
  }
271
304
  /** Formats whose value is stored text or structure: shown on click, never inline. */
272
- const STORED_FORMATS = new Set(['content', 'json', 'messages', 'parts']);
305
+ const STORED_FORMATS = new Set([
306
+ 'content',
307
+ 'json',
308
+ 'messages',
309
+ 'parts',
310
+ ]);
273
311
  function isStringList(value) {
274
312
  return Array.isArray(value) && value.every((item) => typeof item === 'string');
275
313
  }
@@ -289,5 +327,7 @@ export function traceValueShape(meta, value) {
289
327
  if (isStringList(value))
290
328
  return { kind: 'list', value };
291
329
  const items = meta?.fields ? fieldItems(value) : undefined;
292
- return meta?.fields && items ? { kind: 'fields', fields: meta.fields, items } : { kind: 'stored' };
330
+ return meta?.fields && items
331
+ ? { kind: 'fields', fields: meta.fields, items }
332
+ : { kind: 'stored' };
293
333
  }
@@ -3,11 +3,22 @@
3
3
  * Tools, reasoning, and mid-turn narration stay inside the assistant turn
4
4
  * (Seance-style), not as separate messages.
5
5
  */
6
- import type { TranscriptBlock } from '@theoremjs/agents/interface';
6
+ import type { TurnStop } from '@theoremjs/agents';
7
+ import { type TranscriptBlock } from '@theoremjs/agents/interface';
8
+ /** What a reply or a called agent used: its tokens, and its cost when the provider gave one. */
9
+ export type TurnUsage = {
10
+ total: number;
11
+ cost?: {
12
+ usd: number;
13
+ partial?: true;
14
+ };
15
+ };
7
16
  export type TranscriptTurnGroup = {
8
17
  kind: 'user';
9
18
  key: string;
10
19
  blocks: TranscriptBlock[];
20
+ /** How its turn stopped short before any reply. A message with a stop is closed. */
21
+ stop?: TurnStop;
11
22
  } | {
12
23
  kind: 'assistant';
13
24
  key: string;
@@ -15,6 +26,10 @@ export type TranscriptTurnGroup = {
15
26
  /** The reply's work and when it stopped, from its latest stamped `turn-done`. */
16
27
  workedMs?: number;
17
28
  endedAt?: number;
29
+ /** What the reply used, summed over its runs: a reply resumed at a gate has several. */
30
+ usage?: TurnUsage;
31
+ /** How its latest run stopped. */
32
+ stop?: TurnStop;
18
33
  };
19
34
  export type TraceItem = {
20
35
  kind: 'reasoning';
@@ -43,7 +58,12 @@ export type ComposedAssistantTurn = {
43
58
  /** True when the disclosure control should appear. */
44
59
  hasTrace: boolean;
45
60
  };
46
- export declare function groupTranscriptBlocks(blocks: readonly TranscriptBlock[]): TranscriptTurnGroup[];
61
+ /**
62
+ * Blocks as turns: neighbours from the same side share one, unless a block is
63
+ * in `breaks`, which always starts its own. A message whose turn stopped short
64
+ * is closed: the next message starts its own turn.
65
+ */
66
+ export declare function groupTranscriptBlocks(blocks: readonly TranscriptBlock[], breaks?: ReadonlySet<string>): TranscriptTurnGroup[];
47
67
  /** A reply's text to copy; a failure is the message's status, not part of the reply. */
48
68
  export declare function assistantTurnCopyText(blocks: readonly TranscriptBlock[]): string;
49
69
  /** A turn's work, for the status line: running, or done (with its duration when known). */
@@ -98,6 +118,12 @@ export type TurnSpan = {
98
118
  export declare function replyKey(groups: readonly TranscriptTurnGroup[], index: number): string;
99
119
  /** The key of the reply to `prompt`, before and after it streams. */
100
120
  export declare function promptReplyKey(prompt: TranscriptTurnGroup): string;
121
+ /**
122
+ * When the prompt at `index` was sent. Once its reply has stopped, its blocks
123
+ * date it (less any approval waits), so a restored chat keeps its times; until
124
+ * then, when this session first showed it.
125
+ */
126
+ export declare function promptTime(groups: readonly TranscriptTurnGroup[], index: number, timeOf: (key: string) => number): number;
101
127
  /**
102
128
  * Key and timing for the assistant group at `index`. A stopped reply reads its
103
129
  * time from its blocks; a live one counts from its span in this session.
@@ -3,7 +3,20 @@
3
3
  * Tools, reasoning, and mid-turn narration stay inside the assistant turn
4
4
  * (Seance-style), not as separate messages.
5
5
  */
6
+ import { WORDED_STOP_KINDS } from '@theoremjs/agents/interface';
6
7
  import { humanize } from "./shaped-data.js";
8
+ /** `used` with `more` added. A cost only one side reported is partial. */
9
+ function addUsage(used, more) {
10
+ if (!used)
11
+ return more;
12
+ const total = used.total + more.total;
13
+ if (!used.cost && !more.cost)
14
+ return { total };
15
+ const usd = (used.cost?.usd ?? 0) + (more.cost?.usd ?? 0);
16
+ const partial = !used.cost || !more.cost || used.cost.partial || more.cost.partial;
17
+ return { total, cost: { usd, ...(partial ? { partial: true } : {}) } };
18
+ }
19
+ const WORDED = new Set(WORDED_STOP_KINDS);
7
20
  const USER_KINDS = new Set(['user-text', 'user-attachment', 'user-voice']);
8
21
  function isUserTranscriptBlock(block) {
9
22
  return USER_KINDS.has(block.kind);
@@ -12,21 +25,45 @@ function isUserTranscriptBlock(block) {
12
25
  function isHiddenTranscriptBlock(block) {
13
26
  return block.kind === 'turn-done';
14
27
  }
15
- export function groupTranscriptBlocks(blocks) {
28
+ /** A `turn-done` on the turn it closes: a reply's work and usage, or a message's stop. */
29
+ function closeTurn(last, done) {
30
+ if (last?.kind === 'user') {
31
+ if (done.stop && WORDED.has(done.stop.kind))
32
+ last.stop = done.stop;
33
+ return;
34
+ }
35
+ if (!last)
36
+ return;
37
+ if (done.workedMs !== undefined) {
38
+ last.workedMs = done.workedMs;
39
+ last.endedAt = done.endedAt;
40
+ }
41
+ if (done.tokens)
42
+ last.usage = addUsage(last.usage, done.tokens);
43
+ if (done.stop)
44
+ last.stop = done.stop;
45
+ }
46
+ /** Whether `block` joins the turn before it: the same side, and that turn still open. */
47
+ function joinsTurn(last, isUser) {
48
+ if (!last || last.kind !== (isUser ? 'user' : 'assistant'))
49
+ return false;
50
+ return !(last.kind === 'user' && last.stop);
51
+ }
52
+ /**
53
+ * Blocks as turns: neighbours from the same side share one, unless a block is
54
+ * in `breaks`, which always starts its own. A message whose turn stopped short
55
+ * is closed: the next message starts its own turn.
56
+ */
57
+ export function groupTranscriptBlocks(blocks, breaks) {
16
58
  const groups = [];
17
59
  for (const block of blocks) {
18
- if (block.kind === 'turn-done') {
19
- const last = groups.at(-1);
20
- if (last?.kind === 'assistant' && block.workedMs !== undefined) {
21
- last.workedMs = block.workedMs;
22
- last.endedAt = block.endedAt;
23
- }
24
- }
60
+ if (block.kind === 'turn-done')
61
+ closeTurn(groups.at(-1), block);
25
62
  if (isHiddenTranscriptBlock(block))
26
63
  continue;
27
64
  const isUser = isUserTranscriptBlock(block);
28
65
  const last = groups.at(-1);
29
- if (last && last.kind === (isUser ? 'user' : 'assistant')) {
66
+ if (last && joinsTurn(last, isUser) && !breaks?.has(block.id)) {
30
67
  last.blocks.push(block);
31
68
  continue;
32
69
  }
@@ -66,7 +103,7 @@ function lastToolIndexOf(blocks) {
66
103
  return -1;
67
104
  }
68
105
  function pushTextBlock(block, args, trace, body) {
69
- // Text before a tool call is narration; text after the latest one is the
106
+ // invariant: Text before a tool call is narration; text after the latest one is the
70
107
  // answer, streamed in place. If another tool call follows, it becomes narration.
71
108
  if (args.hasTools && args.index < args.lastToolIndex) {
72
109
  if (block.text.trim()) {
@@ -105,7 +142,7 @@ function classifyNonGateBlock(block, args, trace, body) {
105
142
  */
106
143
  export function composeAssistantTurn(blocks) {
107
144
  const visible = blocks.filter((block) => !isHiddenTranscriptBlock(block));
108
- // One card at a time: the gate answered next. The step's later gates wait in the trace.
145
+ // why: One card at a time: the gate answered next. The step's later gates wait in the trace.
109
146
  const nextGate = visible.find(isGatedTool);
110
147
  const gatedTools = nextGate ? [nextGate] : [];
111
148
  const nonGate = visible.filter((block) => block !== nextGate);
@@ -140,6 +177,18 @@ export function replyKey(groups, index) {
140
177
  export function promptReplyKey(prompt) {
141
178
  return `${prompt.key}:reply`;
142
179
  }
180
+ /**
181
+ * When the prompt at `index` was sent. Once its reply has stopped, its blocks
182
+ * date it (less any approval waits), so a restored chat keeps its times; until
183
+ * then, when this session first showed it.
184
+ */
185
+ export function promptTime(groups, index, timeOf) {
186
+ const reply = groups[index + 1];
187
+ if (reply?.kind === 'assistant' && reply.endedAt !== undefined && reply.workedMs !== undefined) {
188
+ return reply.endedAt - reply.workedMs;
189
+ }
190
+ return timeOf(groups[index]?.key ?? '');
191
+ }
143
192
  /**
144
193
  * Key and timing for the assistant group at `index`. A stopped reply reads its
145
194
  * time from its blocks; a live one counts from its span in this session.
@@ -161,7 +210,8 @@ export function assistantTurnTiming(args) {
161
210
  }
162
211
  export function toolCallLabel(tool) {
163
212
  const phase = tool.state?.phase;
164
- if (phase === 'error' || phase === 'cancel')
213
+ if (phase === 'cancel')
165
214
  return humanize(tool.name);
166
- return (phase === 'complete' ? tool.activityPast : tool.activity) ?? humanize(tool.name);
215
+ const settled = phase === 'complete' || phase === 'error';
216
+ return (settled ? tool.activityPast : tool.activity) ?? humanize(tool.name);
167
217
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The transcript's blocks as the wire carries them: the host stores them, and
3
+ * the browser reads the host's conversation back through this schema.
4
+ *
5
+ * @module
6
+ */
7
+ import { z } from '@theoremjs/agents';
8
+ import type { TranscriptBlock } from '@theoremjs/agents/interface';
9
+ /** One transcript block, as a host stores it and the browser reads it back. */
10
+ export declare const transcriptBlockSchema: z.ZodType<TranscriptBlock>;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * The transcript's blocks as the wire carries them: the host stores them, and
3
+ * the browser reads the host's conversation back through this schema.
4
+ *
5
+ * @module
6
+ */
7
+ import { reasoningPartSchema, TURN_STOP_KINDS, turnEventSchema, z, } from '@theoremjs/agents';
8
+ import { guardrailEventSchema } from '@theoremjs/agents/guardrails';
9
+ // why: The block shapes' event parts have one schema, the turn event's; a probe event reuses it.
10
+ function eventPart(probe) {
11
+ return z.custom((value) => turnEventSchema.safeParse(probe(value)).success);
12
+ }
13
+ const TOOL_CALL_STATE_PHASES = [
14
+ 'running',
15
+ 'gate',
16
+ 'complete',
17
+ 'error',
18
+ 'cancel',
19
+ ];
20
+ const toolCallState = z.custom((value) => {
21
+ const parsed = turnEventSchema.safeParse({ type: 'tool', tool: value });
22
+ if (!parsed.success || parsed.data.type !== 'tool' || !('phase' in parsed.data.tool)) {
23
+ return false;
24
+ }
25
+ const { phase } = parsed.data.tool;
26
+ return TOOL_CALL_STATE_PHASES.some((statePhase) => statePhase === phase);
27
+ });
28
+ const argumentsRecord = z.record(z.string(), z.unknown());
29
+ const toolCall = z.object({
30
+ operationId: z.string().optional(),
31
+ name: z.string(),
32
+ callId: z.string(),
33
+ arguments: argumentsRecord,
34
+ draft: z.string().optional(),
35
+ reasoning: z.array(reasoningPartSchema).optional(),
36
+ stepId: z.string().optional(),
37
+ edited: z.object({ from: argumentsRecord, to: argumentsRecord }).optional(),
38
+ activity: z.string().optional(),
39
+ activityPast: z.string().optional(),
40
+ outputDraft: z.string().optional(),
41
+ state: toolCallState.optional(),
42
+ startedAt: z.number().optional(),
43
+ endedAt: z.number().optional(),
44
+ artifacts: z.array(z.unknown()),
45
+ });
46
+ true;
47
+ const turnStop = z.object({ kind: z.enum(TURN_STOP_KINDS), native: z.string().optional() });
48
+ true;
49
+ const blockId = { id: z.string() };
50
+ const transcriptBlock = z.discriminatedUnion('kind', [
51
+ z.object({ ...blockId, kind: z.literal('user-text'), text: z.string() }),
52
+ z.object({
53
+ ...blockId,
54
+ kind: z.enum(['user-attachment', 'user-voice']),
55
+ name: z.string(),
56
+ mimeType: z.string(),
57
+ sizeBytes: z.number(),
58
+ data: z.string().optional(),
59
+ }),
60
+ z.object({ ...blockId, kind: z.literal('thought'), text: z.string() }),
61
+ z.object({ ...blockId, kind: z.literal('text'), text: z.string() }),
62
+ z.object({ ...blockId, kind: z.literal('tool'), tool: toolCall }),
63
+ z.object({
64
+ ...blockId,
65
+ kind: z.literal('structured'),
66
+ value: z.unknown(),
67
+ draft: z.string().optional(),
68
+ }),
69
+ z.object({
70
+ ...blockId,
71
+ kind: z.literal('media'),
72
+ mimeType: z.string(),
73
+ data: z.string().optional(),
74
+ url: z.string().optional(),
75
+ previewUrl: z.string().optional(),
76
+ }),
77
+ z.object({
78
+ ...blockId,
79
+ kind: z.literal('grounding'),
80
+ grounding: eventPart((grounding) => ({
81
+ type: 'grounding',
82
+ grounding,
83
+ })),
84
+ }),
85
+ z.object({
86
+ ...blockId,
87
+ kind: z.literal('citation'),
88
+ sources: eventPart((sources) => ({
89
+ type: 'citation',
90
+ sources,
91
+ })),
92
+ callId: z.string().optional(),
93
+ }),
94
+ z.object({
95
+ ...blockId,
96
+ kind: z.literal('evidence'),
97
+ evidence: eventPart((evidence) => ({
98
+ type: 'evidence',
99
+ evidence,
100
+ })),
101
+ }),
102
+ z.object({ ...blockId, kind: z.literal('guardrail'), guardrail: guardrailEventSchema }),
103
+ z.object({ ...blockId, kind: z.literal('error'), message: z.string() }),
104
+ z.object({
105
+ ...blockId,
106
+ kind: z.literal('turn-done'),
107
+ stop: turnStop.optional(),
108
+ tokens: eventPart((tokens) => ({
109
+ type: 'tokens',
110
+ tokens,
111
+ })).optional(),
112
+ interactionId: z.string().optional(),
113
+ compaction: z.boolean().optional(),
114
+ workedMs: z.number().optional(),
115
+ endedAt: z.number().optional(),
116
+ }),
117
+ ]);
118
+ true;
119
+ /** One transcript block, as a host stores it and the browser reads it back. */
120
+ export const transcriptBlockSchema = transcriptBlock;