@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,4 +1,4 @@
1
- /** Why recording failed; the user reads lexicon `voice.<code>`. */
1
+ /** Why recording failed; the user reads labels `voice.<code>`. */
2
2
  export type VoiceRecorderFailureCode = 'unsupported' | 'permission' | 'unavailable' | 'failed' | 'empty';
3
3
  /** Headless MediaRecorder session for turn-composer voice notes. */
4
4
  export declare class ComposerVoiceRecorder {
@@ -41,7 +41,6 @@ export class ComposerVoiceRecorder {
41
41
  this.accept = accept;
42
42
  this.onLevel = onLevel;
43
43
  }
44
- // fallow-ignore-next-line unused-class-member -- called from useComposerVoice via recorder refs
45
44
  async start() {
46
45
  this.disposeTracks();
47
46
  const mime = pickMediaRecorderMime(this.accept);
@@ -72,10 +71,9 @@ export class ComposerVoiceRecorder {
72
71
  this.recording = true;
73
72
  this.startMeter();
74
73
  }
75
- // fallow-ignore-next-line unused-class-member -- called from useComposerVoice via recorder refs
76
74
  async stop() {
77
75
  if (!this.recorder || !this.recording) {
78
- // lexicon-exempt: internal diagnostic; the user reads voice.empty
76
+ // labels-exempt: internal diagnostic; the user reads voice.empty
79
77
  throw new VoiceRecorderFailure('empty', 'stop without a recording');
80
78
  }
81
79
  const recorder = this.recorder;
@@ -110,14 +108,13 @@ export class ComposerVoiceRecorder {
110
108
  this.recorder.stop();
111
109
  }
112
110
  catch {
113
- // ignore
111
+ // why: stop() throws when the recorder stopped between the check and the call; it is stopped either way.
114
112
  }
115
113
  }
116
114
  this.disposeTracks();
117
115
  this.chunks = [];
118
116
  this.recorder = null;
119
117
  }
120
- // fallow-ignore-next-line unused-class-member -- called from useComposerVoice cleanup
121
118
  dispose() {
122
119
  this.cancel();
123
120
  }
@@ -32,7 +32,7 @@ export function issueSummary(error) {
32
32
  return issuesSummary(error.issues, []);
33
33
  }
34
34
  function badResponse(what, error) {
35
- // lexicon-exempt: internal diagnostic; the user reads error.bad_response
35
+ // labels-exempt: internal diagnostic; the user reads error.bad_response
36
36
  return new TheoremError('bad_response', `${what} failed its wire check: ${issueSummary(error)}`);
37
37
  }
38
38
  /** `raw` as `schema` reads it; one that fails is `bad_response`, naming `what`. */
@@ -51,7 +51,7 @@ function malformed(message) {
51
51
  /** One wire value: a known kind that passed its schema, `unsupported`, or `malformed`. */
52
52
  export function readWireValue(lines, raw) {
53
53
  const kind = wireKind.safeParse(raw);
54
- // lexicon-exempt: internal diagnostic; the user reads session.part_skipped
54
+ // labels-exempt: internal diagnostic; the user reads session.part_skipped
55
55
  if (!kind.success)
56
56
  return malformed(`a line without a kind failed its wire check: ${issueSummary(kind.error)}`);
57
57
  const { type } = kind.data;
@@ -60,7 +60,7 @@ export function readWireValue(lines, raw) {
60
60
  const line = lines[type].safeParse(raw);
61
61
  if (line.success)
62
62
  return line.data;
63
- // lexicon-exempt: internal diagnostic; the user reads session.part_skipped
63
+ // labels-exempt: internal diagnostic; the user reads session.part_skipped
64
64
  return malformed(`a '${type}' line failed its wire check: ${issueSummary(line.error)}`);
65
65
  }
66
66
  /** JSON text as a value, or undefined when it is not JSON. The parser's own error quotes the text, so it is not kept. */
@@ -77,13 +77,13 @@ function jsonValue(text) {
77
77
  /** One wire line's text: {@link readWireValue} of its JSON, or `malformed` when it is not JSON. */
78
78
  export function readWireLine(lines, text) {
79
79
  const json = jsonValue(text);
80
- // lexicon-exempt: internal diagnostic; the user reads session.part_skipped
80
+ // labels-exempt: internal diagnostic; the user reads session.part_skipped
81
81
  return json ? readWireValue(lines, json.value) : malformed('a line is not JSON');
82
82
  }
83
83
  /** A whole reply's JSON text as a value; text that is not JSON is `bad_response`. */
84
84
  export function parseWireJson(text) {
85
85
  const json = jsonValue(text);
86
- // lexicon-exempt: internal diagnostic; the user reads error.bad_response
86
+ // labels-exempt: internal diagnostic; the user reads error.bad_response
87
87
  if (!json)
88
88
  throw new TheoremError('bad_response', 'a line is not JSON');
89
89
  return json.value;
@@ -4,13 +4,13 @@
4
4
  * by URL (see `../mic-capture.ts`), so it imports nothing and ships as is.
5
5
  */
6
6
  registerProcessor(
7
- 'mic-capture-processor',
8
- class MicCaptureProcessor extends AudioWorkletProcessor {
9
- process(inputs) {
10
- const channel = inputs[0][0];
11
- const copy = new Float32Array(channel);
12
- this.port.postMessage(copy, [copy.buffer]);
13
- return true;
14
- }
15
- },
7
+ 'mic-capture-processor',
8
+ class MicCaptureProcessor extends AudioWorkletProcessor {
9
+ process(inputs) {
10
+ const channel = inputs[0][0];
11
+ const copy = new Float32Array(channel);
12
+ this.port.postMessage(copy, [copy.buffer]);
13
+ return true;
14
+ }
15
+ },
16
16
  );
@@ -1,10 +1,21 @@
1
+ import { type RefObject } from 'react';
1
2
  import { type InkWaveStatus } from '../client/ink-waveform.ts';
3
+ /** Mic and speaker levels. A host on the audio clock writes this; the bars read it each frame. */
4
+ export type InkWaveLevels = {
5
+ input: number;
6
+ output: number;
7
+ };
2
8
  export type InkWaveformProps = {
3
9
  status?: InkWaveStatus;
4
10
  inputLevel?: number;
5
11
  outputLevel?: number;
12
+ /**
13
+ * Latest levels, read each frame. When set, it wins over `inputLevel` and
14
+ * `outputLevel`, so a host can move the bars without rendering.
15
+ */
16
+ levelsRef?: RefObject<InkWaveLevels>;
6
17
  toolActive?: boolean;
7
18
  frozen?: boolean;
8
19
  variant?: 'default' | 'hero' | 'pill' | 'strip';
9
20
  };
10
- export declare function InkWaveform({ status, inputLevel, outputLevel, toolActive, frozen, variant, }: InkWaveformProps): import("react").JSX.Element;
21
+ export declare function InkWaveform({ status, inputLevel, outputLevel, levelsRef, toolActive, frozen, variant, }: InkWaveformProps): import("react").JSX.Element;
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useEffect, useMemo, useRef, useState } from 'react';
3
- import { computeInkBarTargets, INK_WAVE_BAR_COUNT, INK_WAVE_HERO_BAR_COUNT, inkWaveDriver, inkWavePhases, stepInkBarHeights, } from "../client/ink-waveform.js";
2
+ import { useEffect, useMemo, useRef } from 'react';
3
+ import { computeInkBarTargets, INK_WAVE_BAR_COUNT, INK_WAVE_HERO_BAR_COUNT, inkWaveDriver, inkWavePhases, } from "../client/ink-waveform.js";
4
+ const REST_HEIGHT = 0.06;
4
5
  const WAVE_CONFIG = {
5
6
  hero: {
6
7
  viewWidth: 960,
@@ -16,7 +17,6 @@ const WAVE_CONFIG = {
16
17
  strokeWidth: 3,
17
18
  barCount: INK_WAVE_BAR_COUNT,
18
19
  },
19
- // A long, low band (a call bar along the foot of a page): many fine bars.
20
20
  strip: {
21
21
  viewWidth: 1600,
22
22
  viewHeight: 48,
@@ -37,62 +37,84 @@ function resolveWaveDimensions(variant) {
37
37
  const gap = (cfg.viewWidth - cfg.strokeWidth * cfg.barCount) / (cfg.barCount + 1);
38
38
  return { ...cfg, gap };
39
39
  }
40
- function useAnimatedWaveHeights(args) {
41
- const { barCount, phases, status, inputLevel, outputLevel, toolActive, frozen } = args;
42
- const [heights, setHeights] = useState(() => Array.from({ length: barCount }, () => 0.06));
43
- const snapRef = useRef({
44
- status,
45
- inputLevel,
46
- outputLevel,
47
- toolActive,
48
- frozen,
40
+ /** One frame's bar targets. The live ref's levels win over the last render's; a frozen wave moves to none. */
41
+ function frameTargets(snap, phases, timeMs) {
42
+ const { input, output } = snap.levelsRef?.current ?? {
43
+ input: snap.inputLevel,
44
+ output: snap.outputLevel,
45
+ };
46
+ const heard = snap.frozen ? { input: 0, output: 0 } : { input, output };
47
+ return computeInkBarTargets({
48
+ phases,
49
+ timeMs,
50
+ driver: inkWaveDriver(snap.status, snap.toolActive, input, output, snap.frozen),
51
+ inputLevel: heard.input,
52
+ outputLevel: heard.output,
53
+ frozen: snap.frozen,
49
54
  });
55
+ }
56
+ /**
57
+ * Paints bar heights onto the mounted lines. The motion is the same as
58
+ * `computeInkBarTargets` plus the per-frame ease; React never sees a frame,
59
+ * so a strip of bars does not reconcile on the audio clock.
60
+ */
61
+ function usePaintedWave(svgRef, snapRef, phases, barCount, viewHeight) {
50
62
  useEffect(() => {
51
- snapRef.current = { status, inputLevel, outputLevel, toolActive, frozen };
52
- }, [status, inputLevel, outputLevel, toolActive, frozen]);
53
- useEffect(() => {
63
+ const svg = svgRef.current;
64
+ if (!svg)
65
+ return;
66
+ const lines = svg.children;
67
+ const count = Math.min(barCount, lines.length);
68
+ const heights = new Float64Array(count);
69
+ heights.fill(REST_HEIGHT);
70
+ const restY = viewHeight - REST_HEIGHT * (viewHeight - 4);
71
+ for (let index = 0; index < count; index++) {
72
+ lines[index].y2.baseVal.value = restY;
73
+ }
54
74
  let frame = 0;
55
- setHeights(Array.from({ length: barCount }, () => 0.06));
56
75
  const tick = (time) => {
57
76
  const snap = snapRef.current;
58
- setHeights((prev) => {
59
- const current = prev.length === barCount ? prev : Array.from({ length: barCount }, () => 0.06);
60
- const targets = computeInkBarTargets({
61
- phases,
62
- timeMs: time,
63
- driver: inkWaveDriver(snap.status, snap.toolActive, snap.inputLevel, snap.outputLevel, snap.frozen),
64
- inputLevel: snap.frozen ? 0 : snap.inputLevel,
65
- outputLevel: snap.frozen ? 0 : snap.outputLevel,
66
- frozen: snap.frozen,
67
- });
68
- return stepInkBarHeights(current, targets, snap.frozen ? 0.2 : 0.14);
69
- });
77
+ const targets = frameTargets(snap, phases, time);
78
+ const alpha = snap.frozen ? 0.2 : 0.14;
79
+ for (let index = 0; index < count; index++) {
80
+ const height = heights[index] + (targets[index] - heights[index]) * alpha;
81
+ heights[index] = height;
82
+ lines[index].y2.baseVal.value = viewHeight - height * (viewHeight - 4);
83
+ }
70
84
  frame = requestAnimationFrame(tick);
71
85
  };
72
86
  frame = requestAnimationFrame(tick);
73
87
  return () => {
74
88
  cancelAnimationFrame(frame);
75
89
  };
76
- }, [barCount, phases]);
77
- return heights;
90
+ }, [svgRef, snapRef, phases, barCount, viewHeight]);
78
91
  }
79
- export function InkWaveform({ status = 'disconnected', inputLevel = 0, outputLevel = 0, toolActive = false, frozen = false, variant = 'default', }) {
92
+ export function InkWaveform({ status = 'disconnected', inputLevel = 0, outputLevel = 0, levelsRef, toolActive = false, frozen = false, variant = 'default', }) {
80
93
  const { viewWidth, viewHeight, preserveAspect, strokeWidth, barCount, gap } = resolveWaveDimensions(variant);
81
94
  const phases = useMemo(() => inkWavePhases(barCount), [barCount]);
82
- const heights = useAnimatedWaveHeights({
83
- barCount,
84
- phases,
95
+ const xs = useMemo(() => {
96
+ const next = new Array(barCount);
97
+ for (let index = 0; index < barCount; index++) {
98
+ next[index] = gap + index * (strokeWidth + gap) + strokeWidth / 2;
99
+ }
100
+ return next;
101
+ }, [barCount, gap, strokeWidth]);
102
+ const svgRef = useRef(null);
103
+ const snapRef = useRef({
85
104
  status,
86
105
  inputLevel,
87
106
  outputLevel,
107
+ levelsRef,
88
108
  toolActive,
89
109
  frozen,
90
110
  });
91
- const bars = heights.map((height, index) => {
92
- const x = gap + index * (strokeWidth + gap) + strokeWidth / 2;
93
- const barHeight = height * (viewHeight - 4);
94
- return { x, y2: viewHeight - barHeight };
95
- });
111
+ snapRef.current.status = status;
112
+ snapRef.current.inputLevel = inputLevel;
113
+ snapRef.current.outputLevel = outputLevel;
114
+ snapRef.current.levelsRef = levelsRef;
115
+ snapRef.current.toolActive = toolActive;
116
+ snapRef.current.frozen = frozen;
117
+ usePaintedWave(svgRef, snapRef, phases, barCount, viewHeight);
96
118
  const className = [
97
119
  'ink-wave',
98
120
  variant === 'hero' ? 'ink-wave--hero' : '',
@@ -101,8 +123,9 @@ export function InkWaveform({ status = 'disconnected', inputLevel = 0, outputLev
101
123
  ]
102
124
  .filter(Boolean)
103
125
  .join(' ');
104
- return (_jsx("svg", { className: className, "aria-hidden": "true", viewBox: `0 0 ${String(viewWidth)} ${String(viewHeight)}`, preserveAspectRatio: preserveAspect,
105
- // Attribute defaults for hosts without ink-controls.css (the Astryx UI);
126
+ const restY = viewHeight - REST_HEIGHT * (viewHeight - 4);
127
+ return (_jsx("svg", { ref: svgRef, className: className, "aria-hidden": "true", viewBox: `0 0 ${String(viewWidth)} ${String(viewHeight)}`, preserveAspectRatio: preserveAspect,
128
+ // why: Attribute defaults for hosts without ink-controls.css (the Astryx UI);
106
129
  // the live stylesheet overrides them where it loads.
107
- width: "100%", height: "100%", children: bars.map((bar) => (_jsx("line", { className: "ink-wave__bar", x1: bar.x, x2: bar.x, y1: viewHeight, y2: bar.y2, strokeWidth: strokeWidth, stroke: "currentColor", strokeLinecap: "square" }, `${String(bar.x)}:${String(bar.y2)}`))) }));
130
+ width: "100%", height: "100%", children: xs.map((x) => (_jsx("line", { className: "ink-wave__bar", x1: x, x2: x, y1: viewHeight, y2: restY, strokeWidth: strokeWidth, stroke: "currentColor", strokeLinecap: "square" }, x))) }));
108
131
  }
@@ -1,13 +1,12 @@
1
- import { type MutableRefObject, type Dispatch, type SetStateAction } from 'react';
1
+ import { type Dispatch, type MutableRefObject, type SetStateAction } from 'react';
2
2
  import { type LiveCaptionState } from '../../client/live/live-captions.ts';
3
- import { type LiveFacingMode, type LiveVideoCapture } from '../../client/live/live-video.ts';
4
3
  import type { LiveConnectPhase, LiveSessionStatus } from '../../client/live/live-state.ts';
4
+ import { type LiveFacingMode, type LiveVideoCapture } from '../../client/live/live-video.ts';
5
5
  import type { LiveConnection, LiveSessionClient } from '../../client/live-client.ts';
6
6
  /** Media + session lifecycle handlers for LiveRunner. */
7
7
  export declare function useLiveRunnerControls(args: {
8
8
  clientRef: MutableRefObject<LiveSessionClient | null>;
9
9
  videoCaptureRef: MutableRefObject<LiveVideoCapture | null>;
10
- captionsRef: MutableRefObject<LiveCaptionState>;
11
10
  connectionRef: MutableRefObject<() => LiveConnection | Promise<LiveConnection>>;
12
11
  statusRef: MutableRefObject<LiveSessionStatus>;
13
12
  isMutedRef: MutableRefObject<boolean>;
@@ -40,7 +39,7 @@ export declare function useLiveRunnerControls(args: {
40
39
  }): {
41
40
  teardownSession: () => void;
42
41
  startSession: () => Promise<void>;
43
- handleSendText: () => void;
42
+ handleSendText: (text?: string) => void;
44
43
  handleToggleVideo: () => Promise<void>;
45
44
  handleFlipCamera: () => Promise<void>;
46
45
  handleToggleMic: () => void;
@@ -1,5 +1,5 @@
1
1
  import { useCallback } from 'react';
2
- import { applyLiveTranscript, } from "../../client/live/live-captions.js";
2
+ import { applyLiveTranscript } from "../../client/live/live-captions.js";
3
3
  import { startLiveVideoCapture, } from "../../client/live/live-video.js";
4
4
  /** Media + session lifecycle handlers for LiveRunner. */
5
5
  export function useLiveRunnerControls(args) {
@@ -31,17 +31,13 @@ export function useLiveRunnerControls(args) {
31
31
  args.reportFailure(err);
32
32
  }
33
33
  }, [args]);
34
- const handleSendText = useCallback(() => {
35
- const text = args.textDraft.trim();
36
- if (!text || !args.clientRef.current || !args.sessionActive || !args.textAvailable)
34
+ const handleSendText = useCallback((text) => {
35
+ const message = (typeof text === 'string' ? text : args.textDraft).trim();
36
+ if (!message || !args.clientRef.current || !args.sessionActive || !args.textAvailable)
37
37
  return;
38
- args.clientRef.current.sendText(text);
38
+ args.clientRef.current.sendText(message);
39
39
  args.setTextDraft('');
40
- const next = applyLiveTranscript(args.captionsRef.current, text, true, false, {
41
- forceNew: true,
42
- });
43
- args.captionsRef.current = next;
44
- args.setCaptions(next);
40
+ args.setCaptions((prev) => applyLiveTranscript(prev, message, true, false, { forceNew: true }));
45
41
  }, [args]);
46
42
  const handleToggleVideo = useCallback(async () => {
47
43
  if (!args.clientRef.current || !args.sessionActive || !args.videoAvailable)
@@ -1,12 +1,14 @@
1
1
  import type { LiveProfileInterface } from '@theoremjs/agents/interface';
2
- import type { LiveCaptionState, LiveCaptionTurn } from '../../client/live/live-captions.ts';
2
+ import { type LiveCaptionState, type LiveCaptionTurn } from '../../client/live/live-captions.ts';
3
+ import type { LiveCallOptions } from '../../client/live/live-page-tool.ts';
3
4
  import type { LiveConnection } from '../../client/live-client.ts';
4
5
  /**
5
6
  * Owns LiveRunner state, session client, and stage callbacks.
6
7
  * `connection` resolves what the call opens: a host with a fixed profile returns
7
- * its id; the playground returns its draft as the relay's open message.
8
+ * its id; the studio returns its draft as the relay's open message. `options`
9
+ * are the host's part in the call: its slots, the page's context, and its page tools.
8
10
  */
9
- export declare function useLiveRunnerModel(iface: LiveProfileInterface, connection: () => LiveConnection | Promise<LiveConnection>): {
11
+ export declare function useLiveRunnerModel(iface: LiveProfileInterface, connection: () => LiveConnection | Promise<LiveConnection>, options?: LiveCallOptions): {
10
12
  handle: string;
11
13
  traces: import("../../index.ts").TraceFeed;
12
14
  callStarted: boolean;
@@ -14,10 +16,12 @@ export declare function useLiveRunnerModel(iface: LiveProfileInterface, connecti
14
16
  captions: LiveCaptionState;
15
17
  failure: import("../../index.ts").ClientFailure | null;
16
18
  sessionEnded: string | null;
17
- inputLevel: number;
19
+ levelsRef: import("react").RefObject<{
20
+ input: number;
21
+ output: number;
22
+ }>;
18
23
  isMuted: boolean;
19
24
  isVideoOn: boolean;
20
- outputLevel: number;
21
25
  sessionActive: boolean;
22
26
  liveState: import("../../index.ts").LiveState;
23
27
  activeTool: string | null;
@@ -37,7 +41,7 @@ export declare function useLiveRunnerModel(iface: LiveProfileInterface, connecti
37
41
  video: boolean;
38
42
  }) => void;
39
43
  handleRestart: () => Promise<void>;
40
- handleSendText: () => void;
44
+ handleSendText: (text?: string) => void;
41
45
  handleToggleMic: () => void;
42
46
  handleFlipCamera: () => Promise<void>;
43
47
  handleToggleVideo: () => Promise<void>;
@@ -1,7 +1,9 @@
1
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
1
  import { liveIngressEnabledFromSpec } from '@theoremjs/agents';
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { stashLiveCaptionCall, } from "../../client/live/live-captions.js";
3
4
  import { liveState } from "../../client/live/live-state.js";
4
5
  import { createTraceFeed } from "../../client/trace-feed.js";
6
+ import { usePageToolWarnings } from "../../hooks/use-page-tool-warnings.js";
5
7
  import { useLiveRunnerControls } from "./use-live-runner-controls.js";
6
8
  import { useLiveRunnerGate, useLiveRunnerUiState } from "./use-live-runner-ui.js";
7
9
  import { useLiveSessionClient } from "./use-live-session-client.js";
@@ -20,7 +22,7 @@ function useLiveCallLifecycle(controls, captionsRef, sessionActive, videoAvailab
20
22
  setEnded(false);
21
23
  void controls.startSession();
22
24
  }, [controls, videoAvailable]);
23
- // Camera capture needs a live session, so a video call turns it on once connected.
25
+ // why: Camera capture needs a live session, so a video call turns it on once connected.
24
26
  useEffect(() => {
25
27
  if (!sessionActive || !videoOnConnectRef.current)
26
28
  return;
@@ -35,8 +37,7 @@ function useLiveCallLifecycle(controls, captionsRef, sessionActive, videoAvailab
35
37
  const [pastCalls, setPastCalls] = useState([]);
36
38
  const handleRestart = useCallback(async () => {
37
39
  const previous = captionsRef.current.turns;
38
- if (previous.length > 0)
39
- setPastCalls((calls) => [...calls, previous]);
40
+ setPastCalls((calls) => stashLiveCaptionCall(calls, previous));
40
41
  setEnded(false);
41
42
  await controls.handleRestart();
42
43
  }, [controls, captionsRef]);
@@ -52,10 +53,11 @@ function useLiveCallLifecycle(controls, captionsRef, sessionActive, videoAvailab
52
53
  /**
53
54
  * Owns LiveRunner state, session client, and stage callbacks.
54
55
  * `connection` resolves what the call opens: a host with a fixed profile returns
55
- * its id; the playground returns its draft as the relay's open message.
56
+ * its id; the studio returns its draft as the relay's open message. `options`
57
+ * are the host's part in the call: its slots, the page's context, and its page tools.
56
58
  */
57
- export function useLiveRunnerModel(iface, connection) {
58
- const ui = useLiveRunnerUiState(iface.lexicon);
59
+ export function useLiveRunnerModel(iface, connection, options = {}) {
60
+ const ui = useLiveRunnerUiState(iface.labels);
59
61
  const gate = useLiveRunnerGate({
60
62
  setCaptions: ui.setCaptions,
61
63
  setActiveTool: ui.setActiveTool,
@@ -73,14 +75,16 @@ export function useLiveRunnerModel(iface, connection) {
73
75
  isMuted: ui.isMuted,
74
76
  voiceEnabled: voiceAvailable,
75
77
  }), [ui.activeTool, ui.connectPhase, ui.isMuted, ui.status, voiceAvailable]);
76
- // One feed per runner: every session it opens adds its records.
78
+ // why: One feed per runner: every session it opens adds its records.
77
79
  const traces = useMemo(createTraceFeed, []);
78
80
  const { clientRef, ensureClient, clearClient } = useLiveSessionClient({
81
+ slots: options.slots,
82
+ context: options.context,
83
+ pageTools: options.pageTools,
79
84
  voiceAvailable,
80
85
  traces,
81
86
  handleLiveTurnEvent: gate.handleLiveTurnEvent,
82
87
  waitForGateDecision: gate.waitForGateDecision,
83
- captionsRef: ui.captionsRef,
84
88
  gatePromptRef: gate.gatePromptRef,
85
89
  isMutedRef: ui.isMutedRef,
86
90
  sessionPermissionsRef: ui.sessionPermissionsRef,
@@ -104,7 +108,6 @@ export function useLiveRunnerModel(iface, connection) {
104
108
  const controls = useLiveRunnerControls({
105
109
  clientRef,
106
110
  videoCaptureRef: ui.videoCaptureRef,
107
- captionsRef: ui.captionsRef,
108
111
  connectionRef,
109
112
  statusRef: ui.statusRef,
110
113
  isMutedRef: ui.isMutedRef,
@@ -136,6 +139,17 @@ export function useLiveRunnerModel(iface, connection) {
136
139
  setIsVideoOn: ui.setIsVideoOn,
137
140
  });
138
141
  const { callStarted, ended, pastCalls, startCall, handleEnd, handleRestart } = useLiveCallLifecycle(controls, ui.captionsRef, ui.sessionActive, videoAvailable);
142
+ // why: The page's context is compared by value, so a host may build it inline on each render.
143
+ const contextJson = JSON.stringify(options.context);
144
+ useEffect(() => {
145
+ clientRef.current?.setContext(contextJson === undefined ? undefined : JSON.parse(contextJson));
146
+ }, [clientRef, contextJson]);
147
+ // why: Slots are compared by value too; a changed value takes an open call up again with it.
148
+ const slotsJson = JSON.stringify(options.slots);
149
+ useEffect(() => {
150
+ clientRef.current?.setSlots(slotsJson === undefined ? undefined : JSON.parse(slotsJson));
151
+ }, [clientRef, slotsJson]);
152
+ usePageToolWarnings(iface.tools.page, options.pageTools);
139
153
  return {
140
154
  handle: iface.identity.handle,
141
155
  traces,
@@ -144,10 +158,9 @@ export function useLiveRunnerModel(iface, connection) {
144
158
  captions: ui.captions,
145
159
  failure: ui.failure,
146
160
  sessionEnded: ui.sessionEnded,
147
- inputLevel: ui.inputLevel,
161
+ levelsRef: ui.levelsRef,
148
162
  isMuted: ui.isMuted,
149
163
  isVideoOn: ui.isVideoOn,
150
- outputLevel: ui.outputLevel,
151
164
  sessionActive: ui.sessionActive,
152
165
  liveState: state,
153
166
  activeTool: ui.activeTool,
@@ -159,8 +172,10 @@ export function useLiveRunnerModel(iface, connection) {
159
172
  videoFacingMode: ui.videoFacingMode,
160
173
  videoPreview: ui.videoPreview,
161
174
  voiceAvailable,
162
- // Also after ending (or failing) before the first connect, so the call can't get stuck.
163
- canRestart: !ui.sessionActive && ui.status !== 'connecting' && (ui.everConnected || ended || ui.failure !== null),
175
+ // why: Also after ending (or failing) before the first connect, so the call can't get stuck.
176
+ canRestart: !ui.sessionActive &&
177
+ ui.status !== 'connecting' &&
178
+ (ui.everConnected || ended || ui.failure !== null),
164
179
  gatePrompt: gate.gatePrompt,
165
180
  setTextDraft: ui.setTextDraft,
166
181
  handleEnd,
@@ -1,22 +1,24 @@
1
+ import { type LabelOverrides, type SessionEventOf } from '@theoremjs/agents';
1
2
  import { type Dispatch, type SetStateAction } from 'react';
2
- import { type LexiconOverrides, type SessionEventOf } from '@theoremjs/agents';
3
3
  import { type ClientFailure } from '../../client/failure.ts';
4
4
  import { applyLiveTurnToolEvent } from '../../client/live/apply-live-turn-tool-event.ts';
5
5
  import { type LiveCaptionState } from '../../client/live/live-captions.ts';
6
+ import type { LiveConnectPhase, LiveSessionStatus } from '../../client/live/live-state.ts';
6
7
  import type { LiveGateAnswer, LiveToolGatePrompt } from '../../client/live/live-tool.ts';
7
8
  import type { LiveFacingMode, LiveVideoCapture } from '../../client/live/live-video.ts';
8
- import type { LiveConnectPhase, LiveSessionStatus } from '../../client/live/live-state.ts';
9
9
  import type { ToolGateResolution } from '../../client/tool-resume.ts';
10
- /** UI + media state bag for the live runner. `lexicon` is the interface's: the profile's wording. */
11
- export declare function useLiveRunnerUiState(lexicon: LexiconOverrides): {
10
+ /** UI + media state bag for the live runner. `labels` is the interface's: the profile's wording. */
11
+ export declare function useLiveRunnerUiState(labels: LabelOverrides): {
12
12
  status: LiveSessionStatus;
13
13
  setStatus: Dispatch<SetStateAction<LiveSessionStatus>>;
14
14
  connectPhase: LiveConnectPhase | null;
15
15
  setConnectPhase: Dispatch<SetStateAction<LiveConnectPhase | null>>;
16
- inputLevel: number;
17
- setInputLevel: Dispatch<SetStateAction<number>>;
18
- outputLevel: number;
19
- setOutputLevel: Dispatch<SetStateAction<number>>;
16
+ levelsRef: import("react").RefObject<{
17
+ input: number;
18
+ output: number;
19
+ }>;
20
+ setInputLevel: (value: SetStateAction<number>) => void;
21
+ setOutputLevel: (value: SetStateAction<number>) => void;
20
22
  isMuted: boolean;
21
23
  setIsMuted: Dispatch<SetStateAction<boolean>>;
22
24
  isVideoOn: boolean;
@@ -1,14 +1,22 @@
1
- import { useCallback, useEffect, useRef, useState } from 'react';
2
1
  import { TheoremError } from '@theoremjs/agents';
2
+ import { useCallback, useEffect, useRef, useState, } from 'react';
3
3
  import { clientFailure } from "../../client/failure.js";
4
4
  import { applyLiveTurnToolEvent } from "../../client/live/apply-live-turn-tool-event.js";
5
- import { clearLiveCaptionInterim, emptyLiveCaptionState, } from "../../client/live/live-captions.js";
6
- /** UI + media state bag for the live runner. `lexicon` is the interface's: the profile's wording. */
7
- export function useLiveRunnerUiState(lexicon) {
5
+ import { applyLiveTurnEvent, clearLiveCaptionInterim, emptyLiveCaptionState, } from "../../client/live/live-captions.js";
6
+ /** UI + media state bag for the live runner. `labels` is the interface's: the profile's wording. */
7
+ export function useLiveRunnerUiState(labels) {
8
8
  const [status, setStatus] = useState('disconnected');
9
9
  const [connectPhase, setConnectPhase] = useState(null);
10
- const [inputLevel, setInputLevel] = useState(0);
11
- const [outputLevel, setOutputLevel] = useState(0);
10
+ // why: Levels move the waveform only. Writing them here keeps a sample off the React tree.
11
+ const levelsRef = useRef({ input: 0, output: 0 });
12
+ const setInputLevel = useCallback((value) => {
13
+ const levels = levelsRef.current;
14
+ levels.input = typeof value === 'function' ? value(levels.input) : value;
15
+ }, []);
16
+ const setOutputLevel = useCallback((value) => {
17
+ const levels = levelsRef.current;
18
+ levels.output = typeof value === 'function' ? value(levels.output) : value;
19
+ }, []);
12
20
  const [isMuted, setIsMuted] = useState(false);
13
21
  const [isVideoOn, setIsVideoOn] = useState(false);
14
22
  const [activeTool, setActiveTool] = useState(null);
@@ -36,8 +44,8 @@ export function useLiveRunnerUiState(lexicon) {
36
44
  setEverConnected(true);
37
45
  }, [sessionActive]);
38
46
  const reportFailure = useCallback((err) => {
39
- setFailure(clientFailure(err, lexicon));
40
- }, [lexicon]);
47
+ setFailure(clientFailure(err, labels));
48
+ }, [labels]);
41
49
  const clearFailure = useCallback(() => {
42
50
  setFailure(null);
43
51
  }, []);
@@ -64,9 +72,8 @@ export function useLiveRunnerUiState(lexicon) {
64
72
  setStatus,
65
73
  connectPhase,
66
74
  setConnectPhase,
67
- inputLevel,
75
+ levelsRef,
68
76
  setInputLevel,
69
- outputLevel,
70
77
  setOutputLevel,
71
78
  isMuted,
72
79
  setIsMuted,
@@ -116,6 +123,7 @@ export function useLiveRunnerGate(args) {
116
123
  setGatePrompt(null);
117
124
  }, []);
118
125
  const handleLiveTurnEvent = useCallback((event) => {
126
+ args.setCaptions((prev) => applyLiveTurnEvent(prev, event));
119
127
  applyLiveTurnToolEvent(event, {
120
128
  gateCallId: gatePromptRef.current?.callId,
121
129
  withdrawGate: () => {
@@ -142,7 +150,7 @@ export function useLiveRunnerGate(args) {
142
150
  answerGate(resolution);
143
151
  }, [answerGate]);
144
152
  const cancelGateDecision = useCallback(() => {
145
- // lexicon-exempt: internal diagnostic; the user reads error.cancelled
153
+ // labels-exempt: internal diagnostic; the user reads error.cancelled
146
154
  gateRejectRef.current?.(new TheoremError('cancelled', 'live session ended with a gate open'));
147
155
  gateResolverRef.current = null;
148
156
  gateRejectRef.current = null;
@@ -1,18 +1,18 @@
1
- import { type Dispatch, type MutableRefObject, type SetStateAction } from 'react';
2
1
  import { type SessionEventOf, type TurnEvent } from '@theoremjs/agents';
2
+ import { type Dispatch, type MutableRefObject, type SetStateAction } from 'react';
3
3
  import { type LiveCaptionState } from '../../client/live/live-captions.ts';
4
+ import type { LiveCallOptions } from '../../client/live/live-page-tool.ts';
5
+ import type { LiveConnectPhase, LiveSessionStatus } from '../../client/live/live-state.ts';
4
6
  import type { LiveGateAnswer, LiveToolGatePrompt } from '../../client/live/live-tool.ts';
5
7
  import type { LiveFacingMode, LiveVideoCapture } from '../../client/live/live-video.ts';
6
- import type { LiveConnectPhase, LiveSessionStatus } from '../../client/live/live-state.ts';
7
8
  import { type LiveConnection, LiveSessionClient } from '../../client/live-client.ts';
8
9
  import type { TraceFeed } from '../../client/trace-feed.ts';
9
- export type LiveClientBindings = {
10
+ export type LiveClientBindings = LiveCallOptions & {
10
11
  voiceAvailable: boolean;
11
12
  /** Where the session's trace records go, when the relay delivers them. */
12
13
  traces: TraceFeed;
13
14
  handleLiveTurnEvent: (event: TurnEvent) => void;
14
15
  waitForGateDecision: (prompt: LiveToolGatePrompt) => Promise<LiveGateAnswer>;
15
- captionsRef: MutableRefObject<LiveCaptionState>;
16
16
  gatePromptRef: MutableRefObject<LiveToolGatePrompt | null>;
17
17
  isMutedRef: MutableRefObject<boolean>;
18
18
  sessionPermissionsRef: MutableRefObject<string[]>;