@theoremjs/react 0.0.0-stage → 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 (320) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +772 -2
  3. package/client/attachment-hover-preview.d.ts +11 -0
  4. package/client/attachment-hover-preview.js +36 -0
  5. package/client/audio-level.d.ts +5 -0
  6. package/client/audio-level.js +24 -0
  7. package/client/composer-attachments.d.ts +30 -0
  8. package/client/composer-attachments.js +33 -0
  9. package/client/composer-drawer.d.ts +19 -0
  10. package/client/composer-drawer.js +15 -0
  11. package/client/composer-hints.d.ts +25 -0
  12. package/client/composer-hints.js +20 -0
  13. package/client/composer-primary.d.ts +16 -0
  14. package/client/composer-primary.js +25 -0
  15. package/client/decision-images.d.ts +76 -0
  16. package/client/decision-images.js +139 -0
  17. package/client/decision-transport.d.ts +70 -0
  18. package/client/decision-transport.js +175 -0
  19. package/client/decode-composer-draft.d.ts +11 -0
  20. package/client/decode-composer-draft.js +12 -0
  21. package/client/embedding-input.d.ts +84 -0
  22. package/client/embedding-input.js +194 -0
  23. package/client/embedding-space.d.ts +82 -0
  24. package/client/embedding-space.js +389 -0
  25. package/client/embedding-transport.d.ts +40 -0
  26. package/client/embedding-transport.js +71 -0
  27. package/client/encode-composer-draft.d.ts +9 -0
  28. package/client/encode-composer-draft.js +34 -0
  29. package/client/encode-files.d.ts +18 -0
  30. package/client/encode-files.js +54 -0
  31. package/client/failure.d.ts +29 -0
  32. package/client/failure.js +34 -0
  33. package/client/generation-selection.d.ts +15 -0
  34. package/client/generation-selection.js +24 -0
  35. package/client/host-call.d.ts +51 -0
  36. package/client/host-call.js +47 -0
  37. package/client/host-transport.d.ts +70 -0
  38. package/client/host-transport.js +75 -0
  39. package/client/image-output.d.ts +6 -0
  40. package/client/image-output.js +13 -0
  41. package/client/index.d.ts +28 -0
  42. package/client/index.js +22 -0
  43. package/client/ink-waveform.d.ts +17 -0
  44. package/client/ink-waveform.js +68 -0
  45. package/client/live/apply-live-turn-tool-event.d.ts +14 -0
  46. package/client/live/apply-live-turn-tool-event.js +39 -0
  47. package/client/live/live-captions.d.ts +53 -0
  48. package/client/live/live-captions.js +174 -0
  49. package/client/live/live-mic-forward.d.ts +30 -0
  50. package/client/live/live-mic-forward.js +46 -0
  51. package/client/live/live-page-tool.d.ts +34 -0
  52. package/client/live/live-page-tool.js +11 -0
  53. package/client/live/live-state.d.ts +13 -0
  54. package/client/live/live-state.js +22 -0
  55. package/client/live/live-tool.d.ts +11 -0
  56. package/client/live/live-tool.js +1 -0
  57. package/client/live/live-video.d.ts +10 -0
  58. package/client/live/live-video.js +71 -0
  59. package/client/live/run-live-tool-call.d.ts +22 -0
  60. package/client/live/run-live-tool-call.js +70 -0
  61. package/client/live/use-live-caption-log.d.ts +17 -0
  62. package/client/live/use-live-caption-log.js +51 -0
  63. package/client/live-client.d.ts +189 -0
  64. package/client/live-client.js +829 -0
  65. package/client/live-errors.d.ts +2 -0
  66. package/client/live-errors.js +9 -0
  67. package/client/live-messages.d.ts +148 -0
  68. package/client/live-messages.js +100 -0
  69. package/client/mic-capture.d.ts +4 -0
  70. package/client/mic-capture.js +6 -0
  71. package/client/oauth-popup.d.ts +26 -0
  72. package/client/oauth-popup.js +30 -0
  73. package/client/page-dispatch.d.ts +15 -0
  74. package/client/page-dispatch.js +63 -0
  75. package/client/pcm-downsample.d.ts +4 -0
  76. package/client/pcm-downsample.js +45 -0
  77. package/client/run-commit.d.ts +34 -0
  78. package/client/run-commit.js +49 -0
  79. package/client/run-session.d.ts +79 -0
  80. package/client/run-session.js +207 -0
  81. package/client/schema-fields.d.ts +63 -0
  82. package/client/schema-fields.js +199 -0
  83. package/client/shaped-data.d.ts +118 -0
  84. package/client/shaped-data.js +343 -0
  85. package/client/source-citations.d.ts +13 -0
  86. package/client/source-citations.js +56 -0
  87. package/client/space-geometry.d.ts +39 -0
  88. package/client/space-geometry.js +183 -0
  89. package/client/state-fields.d.ts +16 -0
  90. package/client/state-fields.js +44 -0
  91. package/client/tool-result.d.ts +78 -0
  92. package/client/tool-result.js +293 -0
  93. package/client/tool-resume.d.ts +50 -0
  94. package/client/tool-resume.js +14 -0
  95. package/client/trace-feed.d.ts +13 -0
  96. package/client/trace-feed.js +18 -0
  97. package/client/trace-story.d.ts +163 -0
  98. package/client/trace-story.js +440 -0
  99. package/client/trace-view.d.ts +130 -0
  100. package/client/trace-view.js +333 -0
  101. package/client/transcript-groups.d.ts +140 -0
  102. package/client/transcript-groups.js +217 -0
  103. package/client/transcript-schema.d.ts +10 -0
  104. package/client/transcript-schema.js +120 -0
  105. package/client/transcript-scroll.d.ts +7 -0
  106. package/client/transcript-scroll.js +6 -0
  107. package/client/transport.d.ts +264 -0
  108. package/client/transport.js +330 -0
  109. package/client/turn-client.d.ts +44 -0
  110. package/client/turn-client.js +88 -0
  111. package/client/voice-label.d.ts +2 -0
  112. package/client/voice-label.js +15 -0
  113. package/client/voice-recorder.d.ts +27 -0
  114. package/client/voice-recorder.js +162 -0
  115. package/client/wire-line.d.ts +56 -0
  116. package/client/wire-line.js +90 -0
  117. package/client/worklets/mic-capture.js +16 -0
  118. package/components/InkWaveform.d.ts +21 -0
  119. package/components/InkWaveform.js +131 -0
  120. package/components/live/use-live-runner-controls.d.ts +47 -0
  121. package/components/live/use-live-runner-controls.js +96 -0
  122. package/components/live/use-live-runner-model.d.ts +49 -0
  123. package/components/live/use-live-runner-model.js +190 -0
  124. package/components/live/use-live-runner-ui.d.ts +67 -0
  125. package/components/live/use-live-runner-ui.js +167 -0
  126. package/components/live/use-live-session-client.d.ts +41 -0
  127. package/components/live/use-live-session-client.js +126 -0
  128. package/components/use-composer-voice.d.ts +28 -0
  129. package/components/use-composer-voice.js +90 -0
  130. package/components/use-editor-selection.d.ts +3 -0
  131. package/components/use-editor-selection.js +19 -0
  132. package/components/use-voice-playback.d.ts +17 -0
  133. package/components/use-voice-playback.js +68 -0
  134. package/hooks/use-described.d.ts +12 -0
  135. package/hooks/use-described.js +25 -0
  136. package/hooks/use-embedding-index.d.ts +27 -0
  137. package/hooks/use-embedding-index.js +43 -0
  138. package/hooks/use-one-call.d.ts +31 -0
  139. package/hooks/use-one-call.js +76 -0
  140. package/hooks/use-page-tool-warnings.d.ts +6 -0
  141. package/hooks/use-page-tool-warnings.js +25 -0
  142. package/hooks/use-second-ticker.d.ts +2 -0
  143. package/hooks/use-second-ticker.js +13 -0
  144. package/hooks/use-theorem-chat-actions.d.ts +79 -0
  145. package/hooks/use-theorem-chat-actions.js +287 -0
  146. package/hooks/use-theorem-chat-state.d.ts +66 -0
  147. package/hooks/use-theorem-chat-state.js +122 -0
  148. package/hooks/use-theorem-chat.d.ts +97 -0
  149. package/hooks/use-theorem-chat.js +528 -0
  150. package/hooks/use-theorem-decision.d.ts +18 -0
  151. package/hooks/use-theorem-decision.js +12 -0
  152. package/hooks/use-theorem-embedding.d.ts +18 -0
  153. package/hooks/use-theorem-embedding.js +12 -0
  154. package/hooks/use-theorem-host.d.ts +45 -0
  155. package/hooks/use-theorem-host.js +135 -0
  156. package/hooks/use-theorem-interface.d.ts +22 -0
  157. package/hooks/use-theorem-interface.js +15 -0
  158. package/hooks/use-trace-records.d.ts +4 -0
  159. package/hooks/use-trace-records.js +7 -0
  160. package/index.d.ts +17 -0
  161. package/index.js +17 -0
  162. package/live.d.ts +9 -0
  163. package/live.js +10 -0
  164. package/package.json +74 -4
  165. package/server/checkpoint-ownership.d.ts +6 -0
  166. package/server/checkpoint-ownership.js +42 -0
  167. package/server/conversation-store.d.ts +92 -0
  168. package/server/conversation-store.js +131 -0
  169. package/server/conversation.d.ts +193 -0
  170. package/server/conversation.js +552 -0
  171. package/server/credential-store.d.ts +24 -0
  172. package/server/credential-store.js +14 -0
  173. package/server/decision-handler.d.ts +42 -0
  174. package/server/decision-handler.js +46 -0
  175. package/server/embedding-handler.d.ts +33 -0
  176. package/server/embedding-handler.js +45 -0
  177. package/server/handler.d.ts +152 -0
  178. package/server/handler.js +771 -0
  179. package/server/json-response.d.ts +2 -0
  180. package/server/json-response.js +7 -0
  181. package/server/mod.d.ts +25 -0
  182. package/server/mod.js +23 -0
  183. package/server/page-dispatch.d.ts +22 -0
  184. package/server/page-dispatch.js +127 -0
  185. package/server/request-body.d.ts +2 -0
  186. package/server/request-body.js +42 -0
  187. package/server/request-check.d.ts +21 -0
  188. package/server/request-check.js +49 -0
  189. package/server/session-authority.d.ts +8 -0
  190. package/server/session-authority.js +78 -0
  191. package/server/session-boundary.d.ts +26 -0
  192. package/server/session-boundary.js +64 -0
  193. package/server/session-identity.d.ts +16 -0
  194. package/server/session-identity.js +41 -0
  195. package/server/session-limits.d.ts +12 -0
  196. package/server/session-limits.js +59 -0
  197. package/server/session-store.d.ts +123 -0
  198. package/server/session-store.js +264 -0
  199. package/server/single-action.d.ts +21 -0
  200. package/server/single-action.js +30 -0
  201. package/server/steer-inbox.d.ts +35 -0
  202. package/server/steer-inbox.js +52 -0
  203. package/server/tool-operations.d.ts +6 -0
  204. package/server/tool-operations.js +132 -0
  205. package/server/turn-input.d.ts +10 -0
  206. package/server/turn-input.js +29 -0
  207. package/styles/ink-controls.css +134 -0
  208. package/ui/CardWall.d.ts +10 -0
  209. package/ui/CardWall.js +61 -0
  210. package/ui/ChatComposerBar.d.ts +62 -0
  211. package/ui/ChatComposerBar.js +306 -0
  212. package/ui/ChatTranscript.d.ts +43 -0
  213. package/ui/ChatTranscript.js +527 -0
  214. package/ui/ConsoleFrame.d.ts +104 -0
  215. package/ui/ConsoleFrame.js +146 -0
  216. package/ui/DecisionAnswers.d.ts +53 -0
  217. package/ui/DecisionAnswers.js +233 -0
  218. package/ui/DecisionImages.d.ts +16 -0
  219. package/ui/DecisionImages.js +46 -0
  220. package/ui/EmbeddingDocuments.d.ts +78 -0
  221. package/ui/EmbeddingDocuments.js +278 -0
  222. package/ui/EmbeddingQuery.d.ts +27 -0
  223. package/ui/EmbeddingQuery.js +95 -0
  224. package/ui/EmbeddingResults.d.ts +41 -0
  225. package/ui/EmbeddingResults.js +165 -0
  226. package/ui/EmbeddingSpace.d.ts +26 -0
  227. package/ui/EmbeddingSpace.js +106 -0
  228. package/ui/LiveCaptionsPanel.d.ts +22 -0
  229. package/ui/LiveCaptionsPanel.js +27 -0
  230. package/ui/LiveRunner.d.ts +30 -0
  231. package/ui/LiveRunner.js +180 -0
  232. package/ui/PaneState.d.ts +33 -0
  233. package/ui/PaneState.js +50 -0
  234. package/ui/SchemaFields.d.ts +10 -0
  235. package/ui/SchemaFields.js +150 -0
  236. package/ui/ShapedData.d.ts +11 -0
  237. package/ui/ShapedData.js +196 -0
  238. package/ui/SidePanel.d.ts +126 -0
  239. package/ui/SidePanel.js +123 -0
  240. package/ui/StateFields.d.ts +44 -0
  241. package/ui/StateFields.js +139 -0
  242. package/ui/TheoremChat.d.ts +122 -0
  243. package/ui/TheoremChat.js +134 -0
  244. package/ui/TheoremDecision.d.ts +30 -0
  245. package/ui/TheoremDecision.js +130 -0
  246. package/ui/TheoremEmbedding.d.ts +27 -0
  247. package/ui/TheoremEmbedding.js +122 -0
  248. package/ui/TheoremHost.d.ts +55 -0
  249. package/ui/TheoremHost.js +240 -0
  250. package/ui/ToolGateCard.d.ts +32 -0
  251. package/ui/ToolGateCard.js +107 -0
  252. package/ui/ToolResult.d.ts +10 -0
  253. package/ui/ToolResult.js +62 -0
  254. package/ui/ToolResultCharts.d.ts +5 -0
  255. package/ui/ToolResultCharts.js +103 -0
  256. package/ui/TraceCharts.d.ts +13 -0
  257. package/ui/TraceCharts.js +266 -0
  258. package/ui/TraceGuardrails.d.ts +11 -0
  259. package/ui/TraceGuardrails.js +74 -0
  260. package/ui/TraceInspector.d.ts +16 -0
  261. package/ui/TraceInspector.js +206 -0
  262. package/ui/TraceInspectorPanel.d.ts +46 -0
  263. package/ui/TraceInspectorPanel.js +95 -0
  264. package/ui/TraceOverview.d.ts +38 -0
  265. package/ui/TraceOverview.js +126 -0
  266. package/ui/TraceSpanDetail.d.ts +10 -0
  267. package/ui/TraceSpanDetail.js +190 -0
  268. package/ui/TraceStory.d.ts +20 -0
  269. package/ui/TraceStory.js +108 -0
  270. package/ui/TraceValues.d.ts +36 -0
  271. package/ui/TraceValues.js +123 -0
  272. package/ui/TraceWaterfall.d.ts +13 -0
  273. package/ui/TraceWaterfall.js +135 -0
  274. package/ui/VoiceNote.d.ts +12 -0
  275. package/ui/VoiceNote.js +28 -0
  276. package/ui/approval-heading.d.ts +4 -0
  277. package/ui/approval-heading.js +9 -0
  278. package/ui/arrive.d.ts +18 -0
  279. package/ui/arrive.js +46 -0
  280. package/ui/built/theme.css +792 -0
  281. package/ui/built/theme.css.d.ts +9 -0
  282. package/ui/built/theorem.d.ts +13 -0
  283. package/ui/built/theorem.js +660 -0
  284. package/ui/built/theorem.variants.d.ts +22 -0
  285. package/ui/code-language.d.ts +12 -0
  286. package/ui/code-language.js +50 -0
  287. package/ui/disclosure-motion.d.ts +6 -0
  288. package/ui/disclosure-motion.js +207 -0
  289. package/ui/embedding-space-gl.d.ts +37 -0
  290. package/ui/embedding-space-gl.js +663 -0
  291. package/ui/embedding-space-view.d.ts +8 -0
  292. package/ui/embedding-space-view.js +8 -0
  293. package/ui/glide.d.ts +7 -0
  294. package/ui/glide.js +44 -0
  295. package/ui/icons.d.ts +4 -0
  296. package/ui/icons.js +38 -0
  297. package/ui/index.d.ts +36 -0
  298. package/ui/index.js +39 -0
  299. package/ui/labels-provider.d.ts +16 -0
  300. package/ui/labels-provider.js +64 -0
  301. package/ui/labels.d.ts +1442 -0
  302. package/ui/labels.js +1443 -0
  303. package/ui/markdown-plugins.d.ts +9 -0
  304. package/ui/markdown-plugins.js +13 -0
  305. package/ui/mcp-icon.d.ts +6 -0
  306. package/ui/mcp-icon.js +8 -0
  307. package/ui/row-keys.d.ts +19 -0
  308. package/ui/row-keys.js +33 -0
  309. package/ui/theme.d.ts +10 -0
  310. package/ui/theme.js +13 -0
  311. package/ui/theorem-theme.d.ts +10 -0
  312. package/ui/theorem-theme.js +105 -0
  313. package/ui/trace-palette.d.ts +10 -0
  314. package/ui/trace-palette.js +10 -0
  315. package/ui/trace-patterns.d.ts +40 -0
  316. package/ui/trace-patterns.js +57 -0
  317. package/ui/transcript-copy-text.d.ts +4 -0
  318. package/ui/transcript-copy-text.js +40 -0
  319. package/ui/wall-columns.d.ts +8 -0
  320. package/ui/wall-columns.js +19 -0
@@ -0,0 +1,190 @@
1
+ import { liveIngressEnabledFromSpec } from '@theoremjs/agents';
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { stashLiveCaptionCall, } from "../../client/live/live-captions.js";
4
+ import { liveState } from "../../client/live/live-state.js";
5
+ import { createTraceFeed } from "../../client/trace-feed.js";
6
+ import { usePageToolWarnings } from "../../hooks/use-page-tool-warnings.js";
7
+ import { useLiveRunnerControls } from "./use-live-runner-controls.js";
8
+ import { useLiveRunnerGate, useLiveRunnerUiState } from "./use-live-runner-ui.js";
9
+ import { useLiveSessionClient } from "./use-live-session-client.js";
10
+ /**
11
+ * Calls start from the landing (voice, or video + voice), not on mount. Once
12
+ * started, the call view stays until a reload; ending leaves Start call. Each
13
+ * restart is a new conversation; earlier calls' captions stay, above a divider.
14
+ */
15
+ function useLiveCallLifecycle(controls, captionsRef, sessionActive, videoAvailable) {
16
+ const [callStarted, setCallStarted] = useState(false);
17
+ const [ended, setEnded] = useState(false);
18
+ const videoOnConnectRef = useRef(false);
19
+ const startCall = useCallback((options) => {
20
+ videoOnConnectRef.current = options.video && videoAvailable;
21
+ setCallStarted(true);
22
+ setEnded(false);
23
+ void controls.startSession();
24
+ }, [controls, videoAvailable]);
25
+ // why: Camera capture needs a live session, so a video call turns it on once connected.
26
+ useEffect(() => {
27
+ if (!sessionActive || !videoOnConnectRef.current)
28
+ return;
29
+ videoOnConnectRef.current = false;
30
+ void controls.handleToggleVideo();
31
+ }, [controls, sessionActive]);
32
+ const handleEnd = useCallback(() => {
33
+ videoOnConnectRef.current = false;
34
+ controls.teardownSession();
35
+ setEnded(true);
36
+ }, [controls]);
37
+ const [pastCalls, setPastCalls] = useState([]);
38
+ const handleRestart = useCallback(async () => {
39
+ const previous = captionsRef.current.turns;
40
+ setPastCalls((calls) => stashLiveCaptionCall(calls, previous));
41
+ setEnded(false);
42
+ await controls.handleRestart();
43
+ }, [controls, captionsRef]);
44
+ const teardownSessionRef = useRef(controls.teardownSession);
45
+ teardownSessionRef.current = controls.teardownSession;
46
+ useEffect(() => {
47
+ return () => {
48
+ teardownSessionRef.current();
49
+ };
50
+ }, []);
51
+ return { callStarted, ended, pastCalls, startCall, handleEnd, handleRestart };
52
+ }
53
+ /**
54
+ * Owns LiveRunner state, session client, and stage callbacks.
55
+ * `connection` resolves what the call opens: a host with a fixed profile returns
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.
58
+ */
59
+ export function useLiveRunnerModel(iface, connection, options = {}) {
60
+ const ui = useLiveRunnerUiState(iface.labels);
61
+ const gate = useLiveRunnerGate({
62
+ setCaptions: ui.setCaptions,
63
+ setActiveTool: ui.setActiveTool,
64
+ reportFailure: ui.reportFailure,
65
+ });
66
+ const connectionRef = useRef(connection);
67
+ connectionRef.current = connection;
68
+ const voiceAvailable = liveIngressEnabledFromSpec(iface.live.ingress, 'audio');
69
+ const videoAvailable = liveIngressEnabledFromSpec(iface.live.ingress, 'video');
70
+ const textAvailable = liveIngressEnabledFromSpec(iface.live.ingress, 'text');
71
+ const state = useMemo(() => liveState({
72
+ status: ui.status,
73
+ connectPhase: ui.connectPhase,
74
+ toolName: ui.activeTool,
75
+ isMuted: ui.isMuted,
76
+ voiceEnabled: voiceAvailable,
77
+ }), [ui.activeTool, ui.connectPhase, ui.isMuted, ui.status, voiceAvailable]);
78
+ // why: One feed per runner: every session it opens adds its records.
79
+ const traces = useMemo(createTraceFeed, []);
80
+ const { clientRef, ensureClient, clearClient } = useLiveSessionClient({
81
+ slots: options.slots,
82
+ context: options.context,
83
+ pageTools: options.pageTools,
84
+ voiceAvailable,
85
+ traces,
86
+ handleLiveTurnEvent: gate.handleLiveTurnEvent,
87
+ waitForGateDecision: gate.waitForGateDecision,
88
+ gatePromptRef: gate.gatePromptRef,
89
+ isMutedRef: ui.isMutedRef,
90
+ sessionPermissionsRef: ui.sessionPermissionsRef,
91
+ videoCaptureRef: ui.videoCaptureRef,
92
+ setConnectPhase: ui.setConnectPhase,
93
+ setStatus: ui.setStatus,
94
+ setSessionActive: ui.setSessionActive,
95
+ reportFailure: ui.reportFailure,
96
+ clearFailure: ui.clearFailure,
97
+ reportSessionEnded: ui.reportSessionEnded,
98
+ clearSessionEnded: ui.clearSessionEnded,
99
+ setCaptions: ui.setCaptions,
100
+ setInputLevel: ui.setInputLevel,
101
+ setOutputLevel: ui.setOutputLevel,
102
+ setActiveTool: ui.setActiveTool,
103
+ setSessionPermissions: ui.setSessionPermissions,
104
+ setVideoPreview: ui.setVideoPreview,
105
+ setVideoFacingMode: ui.setVideoFacingMode,
106
+ setIsVideoOn: ui.setIsVideoOn,
107
+ });
108
+ const controls = useLiveRunnerControls({
109
+ clientRef,
110
+ videoCaptureRef: ui.videoCaptureRef,
111
+ connectionRef,
112
+ statusRef: ui.statusRef,
113
+ isMutedRef: ui.isMutedRef,
114
+ sessionPermissionsRef: ui.sessionPermissionsRef,
115
+ ensureClient,
116
+ clearClient,
117
+ cancelGateDecision: gate.cancelGateDecision,
118
+ stopVideo: ui.stopVideo,
119
+ resetCaptions: ui.resetCaptions,
120
+ sessionActive: ui.sessionActive,
121
+ textAvailable,
122
+ videoAvailable,
123
+ voiceAvailable,
124
+ isVideoOn: ui.isVideoOn,
125
+ textDraft: ui.textDraft,
126
+ setTextDraft: ui.setTextDraft,
127
+ setCaptions: ui.setCaptions,
128
+ reportFailure: ui.reportFailure,
129
+ clearFailure: ui.clearFailure,
130
+ setIsMuted: ui.setIsMuted,
131
+ setSessionActive: ui.setSessionActive,
132
+ setSessionPermissions: ui.setSessionPermissions,
133
+ setStatus: ui.setStatus,
134
+ setConnectPhase: ui.setConnectPhase,
135
+ setInputLevel: ui.setInputLevel,
136
+ setOutputLevel: ui.setOutputLevel,
137
+ setVideoPreview: ui.setVideoPreview,
138
+ setVideoFacingMode: ui.setVideoFacingMode,
139
+ setIsVideoOn: ui.setIsVideoOn,
140
+ });
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);
153
+ return {
154
+ handle: iface.identity.handle,
155
+ traces,
156
+ callStarted,
157
+ pastCalls,
158
+ captions: ui.captions,
159
+ failure: ui.failure,
160
+ sessionEnded: ui.sessionEnded,
161
+ levelsRef: ui.levelsRef,
162
+ isMuted: ui.isMuted,
163
+ isVideoOn: ui.isVideoOn,
164
+ sessionActive: ui.sessionActive,
165
+ liveState: state,
166
+ activeTool: ui.activeTool,
167
+ status: ui.status,
168
+ textAvailable,
169
+ textDraft: ui.textDraft,
170
+ toolActive: ui.activeTool !== null || gate.gatePrompt !== null,
171
+ videoAvailable,
172
+ videoFacingMode: ui.videoFacingMode,
173
+ videoPreview: ui.videoPreview,
174
+ voiceAvailable,
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),
179
+ gatePrompt: gate.gatePrompt,
180
+ setTextDraft: ui.setTextDraft,
181
+ handleEnd,
182
+ startCall,
183
+ handleRestart,
184
+ handleSendText: controls.handleSendText,
185
+ handleToggleMic: controls.handleToggleMic,
186
+ handleFlipCamera: controls.handleFlipCamera,
187
+ handleToggleVideo: controls.handleToggleVideo,
188
+ resolveGateDecision: gate.resolveGateDecision,
189
+ };
190
+ }
@@ -0,0 +1,67 @@
1
+ import { type LabelOverrides, type SessionEventOf } from '@theoremjs/agents';
2
+ import { type Dispatch, type SetStateAction } from 'react';
3
+ import { type ClientFailure } from '../../client/failure.ts';
4
+ import { applyLiveTurnToolEvent } from '../../client/live/apply-live-turn-tool-event.ts';
5
+ import { type LiveCaptionState } from '../../client/live/live-captions.ts';
6
+ import type { LiveConnectPhase, LiveSessionStatus } from '../../client/live/live-state.ts';
7
+ import type { LiveGateAnswer, LiveToolGatePrompt } from '../../client/live/live-tool.ts';
8
+ import type { LiveFacingMode, LiveVideoCapture } from '../../client/live/live-video.ts';
9
+ import type { ToolGateResolution } from '../../client/tool-resume.ts';
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
+ status: LiveSessionStatus;
13
+ setStatus: Dispatch<SetStateAction<LiveSessionStatus>>;
14
+ connectPhase: LiveConnectPhase | null;
15
+ setConnectPhase: Dispatch<SetStateAction<LiveConnectPhase | null>>;
16
+ levelsRef: import("react").RefObject<{
17
+ input: number;
18
+ output: number;
19
+ }>;
20
+ setInputLevel: (value: SetStateAction<number>) => void;
21
+ setOutputLevel: (value: SetStateAction<number>) => void;
22
+ isMuted: boolean;
23
+ setIsMuted: Dispatch<SetStateAction<boolean>>;
24
+ isVideoOn: boolean;
25
+ setIsVideoOn: Dispatch<SetStateAction<boolean>>;
26
+ activeTool: string | null;
27
+ setActiveTool: Dispatch<SetStateAction<string | null>>;
28
+ captions: LiveCaptionState;
29
+ setCaptions: Dispatch<SetStateAction<LiveCaptionState>>;
30
+ failure: ClientFailure | null;
31
+ reportFailure: (err: unknown) => void;
32
+ clearFailure: () => void;
33
+ sessionEnded: string | null;
34
+ reportSessionEnded: (session: SessionEventOf<"ended">) => void;
35
+ clearSessionEnded: () => void;
36
+ textDraft: string;
37
+ setTextDraft: Dispatch<SetStateAction<string>>;
38
+ sessionActive: boolean;
39
+ setSessionActive: Dispatch<SetStateAction<boolean>>;
40
+ sessionPermissions: string[];
41
+ setSessionPermissions: Dispatch<SetStateAction<string[]>>;
42
+ everConnected: boolean;
43
+ videoPreview: HTMLVideoElement | null;
44
+ setVideoPreview: Dispatch<SetStateAction<HTMLVideoElement | null>>;
45
+ videoFacingMode: LiveFacingMode;
46
+ setVideoFacingMode: Dispatch<SetStateAction<LiveFacingMode>>;
47
+ videoCaptureRef: import("react").RefObject<LiveVideoCapture | null>;
48
+ captionsRef: import("react").RefObject<LiveCaptionState>;
49
+ isMutedRef: import("react").RefObject<boolean>;
50
+ sessionPermissionsRef: import("react").RefObject<string[]>;
51
+ statusRef: import("react").RefObject<LiveSessionStatus>;
52
+ resetCaptions: () => void;
53
+ stopVideo: () => void;
54
+ };
55
+ /** Gate prompt + live turn-tool event wiring. */
56
+ export declare function useLiveRunnerGate(args: {
57
+ setCaptions: Dispatch<SetStateAction<LiveCaptionState>>;
58
+ setActiveTool: Dispatch<SetStateAction<string | null>>;
59
+ reportFailure: (err: unknown) => void;
60
+ }): {
61
+ gatePrompt: LiveToolGatePrompt | null;
62
+ gatePromptRef: import("react").RefObject<LiveToolGatePrompt | null>;
63
+ handleLiveTurnEvent: (event: Parameters<typeof applyLiveTurnToolEvent>[0]) => void;
64
+ waitForGateDecision: (prompt: LiveToolGatePrompt) => Promise<LiveGateAnswer>;
65
+ resolveGateDecision: (resolution: ToolGateResolution) => void;
66
+ cancelGateDecision: () => void;
67
+ };
@@ -0,0 +1,167 @@
1
+ import { TheoremError } from '@theoremjs/agents';
2
+ import { useCallback, useEffect, useRef, useState, } from 'react';
3
+ import { clientFailure } from "../../client/failure.js";
4
+ import { applyLiveTurnToolEvent } from "../../client/live/apply-live-turn-tool-event.js";
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
+ const [status, setStatus] = useState('disconnected');
9
+ const [connectPhase, setConnectPhase] = useState(null);
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
+ }, []);
20
+ const [isMuted, setIsMuted] = useState(false);
21
+ const [isVideoOn, setIsVideoOn] = useState(false);
22
+ const [activeTool, setActiveTool] = useState(null);
23
+ const [captions, setCaptions] = useState(emptyLiveCaptionState);
24
+ const [failure, setFailure] = useState(null);
25
+ /** The user's line for a session the provider ended after warning it would. */
26
+ const [sessionEnded, setSessionEnded] = useState(null);
27
+ const [textDraft, setTextDraft] = useState('');
28
+ const [sessionActive, setSessionActive] = useState(false);
29
+ const [sessionPermissions, setSessionPermissions] = useState([]);
30
+ const [everConnected, setEverConnected] = useState(false);
31
+ const [videoPreview, setVideoPreview] = useState(null);
32
+ const [videoFacingMode, setVideoFacingMode] = useState('user');
33
+ const videoCaptureRef = useRef(null);
34
+ const captionsRef = useRef(captions);
35
+ const isMutedRef = useRef(isMuted);
36
+ const sessionPermissionsRef = useRef(sessionPermissions);
37
+ const statusRef = useRef(status);
38
+ captionsRef.current = captions;
39
+ isMutedRef.current = isMuted;
40
+ sessionPermissionsRef.current = sessionPermissions;
41
+ statusRef.current = status;
42
+ useEffect(() => {
43
+ if (sessionActive)
44
+ setEverConnected(true);
45
+ }, [sessionActive]);
46
+ const reportFailure = useCallback((err) => {
47
+ setFailure(clientFailure(err, labels));
48
+ }, [labels]);
49
+ const clearFailure = useCallback(() => {
50
+ setFailure(null);
51
+ }, []);
52
+ const reportSessionEnded = useCallback((session) => {
53
+ setSessionEnded(session.message);
54
+ }, []);
55
+ const clearSessionEnded = useCallback(() => {
56
+ setSessionEnded(null);
57
+ }, []);
58
+ const resetCaptions = useCallback(() => {
59
+ setCaptions(emptyLiveCaptionState());
60
+ }, []);
61
+ const stopVideo = useCallback(() => {
62
+ if (videoCaptureRef.current) {
63
+ videoCaptureRef.current.stop();
64
+ videoCaptureRef.current = null;
65
+ }
66
+ setVideoPreview(null);
67
+ setVideoFacingMode('user');
68
+ setIsVideoOn(false);
69
+ }, []);
70
+ return {
71
+ status,
72
+ setStatus,
73
+ connectPhase,
74
+ setConnectPhase,
75
+ levelsRef,
76
+ setInputLevel,
77
+ setOutputLevel,
78
+ isMuted,
79
+ setIsMuted,
80
+ isVideoOn,
81
+ setIsVideoOn,
82
+ activeTool,
83
+ setActiveTool,
84
+ captions,
85
+ setCaptions,
86
+ failure,
87
+ reportFailure,
88
+ clearFailure,
89
+ sessionEnded,
90
+ reportSessionEnded,
91
+ clearSessionEnded,
92
+ textDraft,
93
+ setTextDraft,
94
+ sessionActive,
95
+ setSessionActive,
96
+ sessionPermissions,
97
+ setSessionPermissions,
98
+ everConnected,
99
+ videoPreview,
100
+ setVideoPreview,
101
+ videoFacingMode,
102
+ setVideoFacingMode,
103
+ videoCaptureRef,
104
+ captionsRef,
105
+ isMutedRef,
106
+ sessionPermissionsRef,
107
+ statusRef,
108
+ resetCaptions,
109
+ stopVideo,
110
+ };
111
+ }
112
+ /** Gate prompt + live turn-tool event wiring. */
113
+ export function useLiveRunnerGate(args) {
114
+ const [gatePrompt, setGatePrompt] = useState(null);
115
+ const gatePromptRef = useRef(gatePrompt);
116
+ gatePromptRef.current = gatePrompt;
117
+ const gateResolverRef = useRef(null);
118
+ const gateRejectRef = useRef(null);
119
+ const answerGate = useCallback((answer) => {
120
+ gateResolverRef.current?.(answer);
121
+ gateResolverRef.current = null;
122
+ gateRejectRef.current = null;
123
+ setGatePrompt(null);
124
+ }, []);
125
+ const handleLiveTurnEvent = useCallback((event) => {
126
+ args.setCaptions((prev) => applyLiveTurnEvent(prev, event));
127
+ applyLiveTurnToolEvent(event, {
128
+ gateCallId: gatePromptRef.current?.callId,
129
+ withdrawGate: () => {
130
+ answerGate('withdrawn');
131
+ },
132
+ clearInterim: () => {
133
+ args.setCaptions((prev) => clearLiveCaptionInterim(prev));
134
+ },
135
+ clearActiveTool: () => {
136
+ args.setActiveTool(null);
137
+ },
138
+ reportFailure: args.reportFailure,
139
+ setActiveTool: args.setActiveTool,
140
+ });
141
+ }, [args, answerGate]);
142
+ const waitForGateDecision = useCallback((prompt) => {
143
+ return new Promise((resolve, reject) => {
144
+ setGatePrompt(prompt);
145
+ gateResolverRef.current = resolve;
146
+ gateRejectRef.current = reject;
147
+ });
148
+ }, []);
149
+ const resolveGateDecision = useCallback((resolution) => {
150
+ answerGate(resolution);
151
+ }, [answerGate]);
152
+ const cancelGateDecision = useCallback(() => {
153
+ // labels-exempt: internal diagnostic; the user reads error.cancelled
154
+ gateRejectRef.current?.(new TheoremError('cancelled', 'live session ended with a gate open'));
155
+ gateResolverRef.current = null;
156
+ gateRejectRef.current = null;
157
+ setGatePrompt(null);
158
+ }, []);
159
+ return {
160
+ gatePrompt,
161
+ gatePromptRef,
162
+ handleLiveTurnEvent,
163
+ waitForGateDecision,
164
+ resolveGateDecision,
165
+ cancelGateDecision,
166
+ };
167
+ }
@@ -0,0 +1,41 @@
1
+ import { type SessionEventOf, type TurnEvent } from '@theoremjs/agents';
2
+ import { type Dispatch, type MutableRefObject, type SetStateAction } from 'react';
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';
6
+ import type { LiveGateAnswer, LiveToolGatePrompt } from '../../client/live/live-tool.ts';
7
+ import type { LiveFacingMode, LiveVideoCapture } from '../../client/live/live-video.ts';
8
+ import { type LiveConnection, LiveSessionClient } from '../../client/live-client.ts';
9
+ import type { TraceFeed } from '../../client/trace-feed.ts';
10
+ export type LiveClientBindings = LiveCallOptions & {
11
+ voiceAvailable: boolean;
12
+ /** Where the session's trace records go, when the relay delivers them. */
13
+ traces: TraceFeed;
14
+ handleLiveTurnEvent: (event: TurnEvent) => void;
15
+ waitForGateDecision: (prompt: LiveToolGatePrompt) => Promise<LiveGateAnswer>;
16
+ gatePromptRef: MutableRefObject<LiveToolGatePrompt | null>;
17
+ isMutedRef: MutableRefObject<boolean>;
18
+ sessionPermissionsRef: MutableRefObject<string[]>;
19
+ videoCaptureRef: MutableRefObject<LiveVideoCapture | null>;
20
+ setConnectPhase: Dispatch<SetStateAction<LiveConnectPhase | null>>;
21
+ setStatus: Dispatch<SetStateAction<LiveSessionStatus>>;
22
+ setSessionActive: Dispatch<SetStateAction<boolean>>;
23
+ reportFailure: (err: unknown) => void;
24
+ clearFailure: () => void;
25
+ reportSessionEnded: (session: SessionEventOf<'ended'>) => void;
26
+ clearSessionEnded: () => void;
27
+ setCaptions: Dispatch<SetStateAction<LiveCaptionState>>;
28
+ setInputLevel: Dispatch<SetStateAction<number>>;
29
+ setOutputLevel: Dispatch<SetStateAction<number>>;
30
+ setActiveTool: Dispatch<SetStateAction<string | null>>;
31
+ setSessionPermissions: Dispatch<SetStateAction<string[]>>;
32
+ setVideoPreview: Dispatch<SetStateAction<HTMLVideoElement | null>>;
33
+ setVideoFacingMode: Dispatch<SetStateAction<LiveFacingMode>>;
34
+ setIsVideoOn: Dispatch<SetStateAction<boolean>>;
35
+ };
36
+ /** Owns LiveSessionClient construction + reconnect identity. */
37
+ export declare function useLiveSessionClient(bindings: LiveClientBindings): {
38
+ clientRef: import("react").RefObject<LiveSessionClient | null>;
39
+ ensureClient: (connection: LiveConnection) => LiveSessionClient;
40
+ clearClient: () => void;
41
+ };
@@ -0,0 +1,126 @@
1
+ import { TheoremError } from '@theoremjs/agents';
2
+ import { useCallback, useRef, } from 'react';
3
+ import { applyLiveTranscript } from "../../client/live/live-captions.js";
4
+ import { runLiveToolCall } from "../../client/live/run-live-tool-call.js";
5
+ import { LiveSessionClient } from "../../client/live-client.js";
6
+ function onLiveStatusChange(next, bindings) {
7
+ bindings.setStatus(next);
8
+ if (next === 'listening' || next === 'ready') {
9
+ bindings.setSessionActive(true);
10
+ bindings.clearFailure();
11
+ bindings.clearSessionEnded();
12
+ return;
13
+ }
14
+ if (next !== 'disconnected' && next !== 'error')
15
+ return;
16
+ bindings.setSessionActive(false);
17
+ if (bindings.videoCaptureRef.current) {
18
+ bindings.videoCaptureRef.current.stop();
19
+ bindings.videoCaptureRef.current = null;
20
+ }
21
+ bindings.setVideoPreview(null);
22
+ bindings.setVideoFacingMode('user');
23
+ bindings.setIsVideoOn(false);
24
+ }
25
+ function onLiveTranscript(text, isUser, meta, bindings) {
26
+ // why: A thought or a tool call in the same batch is already queued; each folds onto the last.
27
+ bindings.setCaptions((prev) => applyLiveTranscript(prev, text, isUser, meta?.interim));
28
+ }
29
+ async function onLiveToolCall(name, toolArgs, meta, bindings, clientRef) {
30
+ bindings.setActiveTool(name);
31
+ bindings.clearFailure();
32
+ const client = clientRef.current;
33
+ if (!client) {
34
+ // labels-exempt: internal diagnostic; the user reads error.request
35
+ bindings.reportFailure(new TheoremError('request', 'live tool call with no session client'));
36
+ return;
37
+ }
38
+ try {
39
+ await runLiveToolCall({
40
+ pageTools: bindings.pageTools,
41
+ executeToolOnRelay: (call) => client.executeToolOnRelay(call),
42
+ name,
43
+ toolArgs,
44
+ callId: meta.callId,
45
+ operationId: meta.operationId,
46
+ sessionPermissions: bindings.sessionPermissionsRef.current,
47
+ setSessionPermissions: (next) => {
48
+ bindings.sessionPermissionsRef.current = next;
49
+ bindings.setSessionPermissions(next);
50
+ },
51
+ waitForGateDecision: bindings.waitForGateDecision,
52
+ });
53
+ }
54
+ catch (err) {
55
+ bindings.reportFailure(err);
56
+ }
57
+ finally {
58
+ if (!bindings.gatePromptRef.current)
59
+ bindings.setActiveTool(null);
60
+ }
61
+ }
62
+ /** Owns LiveSessionClient construction + reconnect identity. */
63
+ export function useLiveSessionClient(bindings) {
64
+ const clientRef = useRef(null);
65
+ const bindingsRef = useRef(bindings);
66
+ bindingsRef.current = bindings;
67
+ const ensureClient = useCallback((connection) => {
68
+ if (clientRef.current)
69
+ return clientRef.current;
70
+ const b = bindingsRef.current;
71
+ const client = new LiveSessionClient({
72
+ ...connection,
73
+ slots: b.slots,
74
+ context: b.context,
75
+ voiceIngress: b.voiceAvailable,
76
+ onConnectPhase: (phase) => {
77
+ b.setConnectPhase(phase);
78
+ },
79
+ onStatusChange: (next) => {
80
+ onLiveStatusChange(next, bindingsRef.current);
81
+ },
82
+ onTranscript: (text, isUser, meta) => {
83
+ onLiveTranscript(text, isUser, meta, bindingsRef.current);
84
+ },
85
+ onTurnEvent: (event) => {
86
+ // why: An `unsupported` event is the host's to read; Theorem's UI shows nothing for it.
87
+ if (event.type === 'unsupported')
88
+ return;
89
+ // why: A `malformed` one was left out and the call goes on; the user reads that part was skipped.
90
+ if (event.type === 'malformed') {
91
+ bindingsRef.current.reportFailure(event.error);
92
+ return;
93
+ }
94
+ bindingsRef.current.handleLiveTurnEvent(event);
95
+ },
96
+ onTrace: (record) => {
97
+ bindingsRef.current.traces.push(record);
98
+ },
99
+ onVolumeLevel: (level, isUser) => {
100
+ const current = bindingsRef.current;
101
+ if (isUser) {
102
+ current.setInputLevel(current.isMutedRef.current ? 0 : level);
103
+ }
104
+ else {
105
+ current.setOutputLevel(level);
106
+ }
107
+ },
108
+ onError: (err) => {
109
+ bindingsRef.current.reportFailure(err);
110
+ },
111
+ onSessionEnded: (session) => {
112
+ bindingsRef.current.reportSessionEnded(session);
113
+ },
114
+ onToolCall: (name, args, meta) => onLiveToolCall(name, args, meta, bindingsRef.current, clientRef),
115
+ });
116
+ clientRef.current = client;
117
+ return client;
118
+ }, []);
119
+ const clearClient = useCallback(() => {
120
+ if (clientRef.current) {
121
+ clientRef.current.disconnect();
122
+ clientRef.current = null;
123
+ }
124
+ }, []);
125
+ return { clientRef, ensureClient, clearClient };
126
+ }
@@ -0,0 +1,28 @@
1
+ import { type LabelOverrides } from '@theoremjs/agents';
2
+ import type { ProfileInputsInterface } from '@theoremjs/agents/interface';
3
+ import { type VoiceRecorderFailureCode } from '../client/voice-recorder.ts';
4
+ /** A voice note that could not be recorded or staged. */
5
+ export type VoiceFailure = {
6
+ /** A recorder failure, or `too_many_files` when the message is already full. */
7
+ code: VoiceRecorderFailureCode | 'too_many_files';
8
+ /** What the user reads: the profile labels' `voice.<code>` (or the file-limit line). */
9
+ error: string;
10
+ /** Raw detail for the builder; never shown to the user. */
11
+ errorInternal?: string;
12
+ };
13
+ export declare function useComposerVoice(args: {
14
+ inputs: ProfileInputsInterface;
15
+ /** The interface's `labels`: the profile's wording for voice failures. */
16
+ labels: LabelOverrides;
17
+ pendingFiles: readonly File[];
18
+ onVoiceStaged?: (file: File) => void;
19
+ onVoiceClear?: () => void;
20
+ }): {
21
+ recording: boolean;
22
+ inputLevel: number;
23
+ failure: VoiceFailure | null;
24
+ setFailure: import("react").Dispatch<import("react").SetStateAction<VoiceFailure | null>>;
25
+ toggleRecording: () => Promise<void>;
26
+ discardRecordingOrVoice: () => void;
27
+ disposeRecorder: () => void;
28
+ };