@theoremjs/react 0.0.0-stage → 0.3.0

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 (231) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +246 -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-transport.d.ts +48 -0
  16. package/client/decision-transport.js +102 -0
  17. package/client/decode-composer-draft.d.ts +11 -0
  18. package/client/decode-composer-draft.js +12 -0
  19. package/client/encode-composer-draft.d.ts +9 -0
  20. package/client/encode-composer-draft.js +34 -0
  21. package/client/encode-files.d.ts +18 -0
  22. package/client/encode-files.js +54 -0
  23. package/client/failure.d.ts +32 -0
  24. package/client/failure.js +34 -0
  25. package/client/generation-selection.d.ts +15 -0
  26. package/client/generation-selection.js +24 -0
  27. package/client/host-call.d.ts +51 -0
  28. package/client/host-call.js +42 -0
  29. package/client/host-transport.d.ts +68 -0
  30. package/client/host-transport.js +74 -0
  31. package/client/image-output.d.ts +6 -0
  32. package/client/image-output.js +13 -0
  33. package/client/index.d.ts +20 -0
  34. package/client/index.js +18 -0
  35. package/client/ink-waveform.d.ts +17 -0
  36. package/client/ink-waveform.js +69 -0
  37. package/client/live/apply-live-turn-tool-event.d.ts +14 -0
  38. package/client/live/apply-live-turn-tool-event.js +39 -0
  39. package/client/live/live-captions.d.ts +23 -0
  40. package/client/live/live-captions.js +70 -0
  41. package/client/live/live-mic-forward.d.ts +29 -0
  42. package/client/live/live-mic-forward.js +37 -0
  43. package/client/live/live-state.d.ts +11 -0
  44. package/client/live/live-state.js +20 -0
  45. package/client/live/live-tool.d.ts +11 -0
  46. package/client/live/live-tool.js +1 -0
  47. package/client/live/live-video.d.ts +10 -0
  48. package/client/live/live-video.js +71 -0
  49. package/client/live/run-live-tool-call.d.ts +17 -0
  50. package/client/live/run-live-tool-call.js +32 -0
  51. package/client/live-client.d.ts +141 -0
  52. package/client/live-client.js +638 -0
  53. package/client/live-errors.d.ts +2 -0
  54. package/client/live-errors.js +9 -0
  55. package/client/live-messages.d.ts +110 -0
  56. package/client/live-messages.js +67 -0
  57. package/client/mic-capture.d.ts +4 -0
  58. package/client/mic-capture.js +6 -0
  59. package/client/oauth-popup.d.ts +26 -0
  60. package/client/oauth-popup.js +30 -0
  61. package/client/pcm-downsample.d.ts +4 -0
  62. package/client/pcm-downsample.js +45 -0
  63. package/client/run-commit.d.ts +61 -0
  64. package/client/run-commit.js +124 -0
  65. package/client/run-session.d.ts +80 -0
  66. package/client/run-session.js +215 -0
  67. package/client/schema-fields.d.ts +63 -0
  68. package/client/schema-fields.js +178 -0
  69. package/client/shaped-data.d.ts +118 -0
  70. package/client/shaped-data.js +314 -0
  71. package/client/source-citations.d.ts +13 -0
  72. package/client/source-citations.js +56 -0
  73. package/client/state-fields.d.ts +16 -0
  74. package/client/state-fields.js +44 -0
  75. package/client/tool-result.d.ts +78 -0
  76. package/client/tool-result.js +275 -0
  77. package/client/tool-resume.d.ts +33 -0
  78. package/client/tool-resume.js +11 -0
  79. package/client/trace-feed.d.ts +13 -0
  80. package/client/trace-feed.js +18 -0
  81. package/client/trace-story.d.ts +163 -0
  82. package/client/trace-story.js +416 -0
  83. package/client/trace-view.d.ts +130 -0
  84. package/client/trace-view.js +293 -0
  85. package/client/transcript-groups.d.ts +114 -0
  86. package/client/transcript-groups.js +167 -0
  87. package/client/transcript-scroll.d.ts +7 -0
  88. package/client/transcript-scroll.js +6 -0
  89. package/client/transport.d.ts +176 -0
  90. package/client/transport.js +256 -0
  91. package/client/turn-client.d.ts +35 -0
  92. package/client/turn-client.js +93 -0
  93. package/client/voice-label.d.ts +2 -0
  94. package/client/voice-label.js +15 -0
  95. package/client/voice-recorder.d.ts +27 -0
  96. package/client/voice-recorder.js +165 -0
  97. package/client/walk-away.d.ts +34 -0
  98. package/client/walk-away.js +48 -0
  99. package/client/wire-line.d.ts +56 -0
  100. package/client/wire-line.js +90 -0
  101. package/client/worklets/mic-capture.js +16 -0
  102. package/components/InkWaveform.d.ts +10 -0
  103. package/components/InkWaveform.js +108 -0
  104. package/components/live/use-live-runner-controls.d.ts +48 -0
  105. package/components/live/use-live-runner-controls.js +100 -0
  106. package/components/live/use-live-runner-model.d.ts +45 -0
  107. package/components/live/use-live-runner-model.js +175 -0
  108. package/components/live/use-live-runner-ui.d.ts +65 -0
  109. package/components/live/use-live-runner-ui.js +159 -0
  110. package/components/live/use-live-session-client.d.ts +41 -0
  111. package/components/live/use-live-session-client.js +123 -0
  112. package/components/use-composer-voice.d.ts +28 -0
  113. package/components/use-composer-voice.js +90 -0
  114. package/components/use-editor-selection.d.ts +3 -0
  115. package/components/use-editor-selection.js +16 -0
  116. package/components/use-voice-playback.d.ts +16 -0
  117. package/components/use-voice-playback.js +68 -0
  118. package/hooks/use-described.d.ts +8 -0
  119. package/hooks/use-described.js +23 -0
  120. package/hooks/use-theorem-chat-actions.d.ts +75 -0
  121. package/hooks/use-theorem-chat-actions.js +265 -0
  122. package/hooks/use-theorem-chat-state.d.ts +66 -0
  123. package/hooks/use-theorem-chat-state.js +118 -0
  124. package/hooks/use-theorem-chat.d.ts +63 -0
  125. package/hooks/use-theorem-chat.js +340 -0
  126. package/hooks/use-theorem-decision.d.ts +23 -0
  127. package/hooks/use-theorem-decision.js +75 -0
  128. package/hooks/use-theorem-host.d.ts +44 -0
  129. package/hooks/use-theorem-host.js +117 -0
  130. package/hooks/use-theorem-interface.d.ts +19 -0
  131. package/hooks/use-theorem-interface.js +25 -0
  132. package/hooks/use-trace-records.d.ts +4 -0
  133. package/hooks/use-trace-records.js +7 -0
  134. package/index.d.ts +14 -0
  135. package/index.js +14 -0
  136. package/live.d.ts +8 -0
  137. package/live.js +10 -0
  138. package/package.json +73 -4
  139. package/server/credential-store.d.ts +24 -0
  140. package/server/credential-store.js +14 -0
  141. package/server/decision-handler.d.ts +34 -0
  142. package/server/decision-handler.js +55 -0
  143. package/server/handler.d.ts +114 -0
  144. package/server/handler.js +568 -0
  145. package/server/json-response.d.ts +2 -0
  146. package/server/json-response.js +7 -0
  147. package/server/mod.d.ts +16 -0
  148. package/server/mod.js +15 -0
  149. package/server/request-check.d.ts +16 -0
  150. package/server/request-check.js +34 -0
  151. package/server/session-store.d.ts +74 -0
  152. package/server/session-store.js +49 -0
  153. package/server/steer-inbox.d.ts +35 -0
  154. package/server/steer-inbox.js +52 -0
  155. package/server/walk-away.d.ts +22 -0
  156. package/server/walk-away.js +41 -0
  157. package/styles/ink-controls.css +134 -0
  158. package/ui/ChatComposerBar.d.ts +39 -0
  159. package/ui/ChatComposerBar.js +251 -0
  160. package/ui/ChatTranscript.d.ts +30 -0
  161. package/ui/ChatTranscript.js +474 -0
  162. package/ui/ConsoleFrame.d.ts +47 -0
  163. package/ui/ConsoleFrame.js +46 -0
  164. package/ui/DecisionAnswers.d.ts +15 -0
  165. package/ui/DecisionAnswers.js +102 -0
  166. package/ui/LiveRunner.d.ts +28 -0
  167. package/ui/LiveRunner.js +193 -0
  168. package/ui/SchemaFields.d.ts +10 -0
  169. package/ui/SchemaFields.js +122 -0
  170. package/ui/ShapedData.d.ts +11 -0
  171. package/ui/ShapedData.js +151 -0
  172. package/ui/SidePanel.d.ts +57 -0
  173. package/ui/SidePanel.js +50 -0
  174. package/ui/StateFields.d.ts +36 -0
  175. package/ui/StateFields.js +96 -0
  176. package/ui/TheoremChat.d.ts +71 -0
  177. package/ui/TheoremChat.js +146 -0
  178. package/ui/TheoremDecision.d.ts +38 -0
  179. package/ui/TheoremDecision.js +87 -0
  180. package/ui/TheoremHost.d.ts +38 -0
  181. package/ui/TheoremHost.js +150 -0
  182. package/ui/ToolGateCard.d.ts +28 -0
  183. package/ui/ToolGateCard.js +100 -0
  184. package/ui/ToolResult.d.ts +10 -0
  185. package/ui/ToolResult.js +60 -0
  186. package/ui/ToolResultCharts.d.ts +5 -0
  187. package/ui/ToolResultCharts.js +86 -0
  188. package/ui/TraceCharts.d.ts +13 -0
  189. package/ui/TraceCharts.js +235 -0
  190. package/ui/TraceGuardrails.d.ts +10 -0
  191. package/ui/TraceGuardrails.js +56 -0
  192. package/ui/TraceInspector.d.ts +6 -0
  193. package/ui/TraceInspector.js +189 -0
  194. package/ui/TraceInspectorPanel.d.ts +24 -0
  195. package/ui/TraceInspectorPanel.js +57 -0
  196. package/ui/TraceOverview.d.ts +34 -0
  197. package/ui/TraceOverview.js +96 -0
  198. package/ui/TraceSpanDetail.d.ts +10 -0
  199. package/ui/TraceSpanDetail.js +188 -0
  200. package/ui/TraceStory.d.ts +20 -0
  201. package/ui/TraceStory.js +107 -0
  202. package/ui/TraceValues.d.ts +36 -0
  203. package/ui/TraceValues.js +113 -0
  204. package/ui/TraceWaterfall.d.ts +13 -0
  205. package/ui/TraceWaterfall.js +132 -0
  206. package/ui/VoiceNote.d.ts +12 -0
  207. package/ui/VoiceNote.js +28 -0
  208. package/ui/built/theme.css +774 -0
  209. package/ui/built/theorem.d.ts +13 -0
  210. package/ui/built/theorem.js +631 -0
  211. package/ui/built/theorem.variants.d.ts +16 -0
  212. package/ui/disclosure-motion.d.ts +6 -0
  213. package/ui/disclosure-motion.js +163 -0
  214. package/ui/icons.d.ts +4 -0
  215. package/ui/icons.js +37 -0
  216. package/ui/index.d.ts +25 -0
  217. package/ui/index.js +29 -0
  218. package/ui/labels-provider.d.ts +16 -0
  219. package/ui/labels-provider.js +61 -0
  220. package/ui/labels.d.ts +1153 -0
  221. package/ui/labels.js +603 -0
  222. package/ui/theme.d.ts +10 -0
  223. package/ui/theme.js +13 -0
  224. package/ui/theorem-theme.d.ts +10 -0
  225. package/ui/theorem-theme.js +83 -0
  226. package/ui/trace-palette.d.ts +24 -0
  227. package/ui/trace-palette.js +24 -0
  228. package/ui/trace-patterns.d.ts +32 -0
  229. package/ui/trace-patterns.js +44 -0
  230. package/ui/transcript-copy-text.d.ts +4 -0
  231. package/ui/transcript-copy-text.js +39 -0
@@ -0,0 +1,45 @@
1
+ import type { LiveProfileInterface } from '@theoremjs/agents/interface';
2
+ import type { LiveCaptionState, LiveCaptionTurn } from '../../client/live/live-captions.ts';
3
+ import type { LiveConnection } from '../../client/live-client.ts';
4
+ /**
5
+ * Owns LiveRunner state, session client, and stage callbacks.
6
+ * `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
+ */
9
+ export declare function useLiveRunnerModel(iface: LiveProfileInterface, connection: () => LiveConnection | Promise<LiveConnection>): {
10
+ handle: string;
11
+ traces: import("../../index.ts").TraceFeed;
12
+ callStarted: boolean;
13
+ pastCalls: LiveCaptionTurn[][];
14
+ captions: LiveCaptionState;
15
+ failure: import("../../index.ts").ClientFailure | null;
16
+ sessionEnded: string | null;
17
+ inputLevel: number;
18
+ isMuted: boolean;
19
+ isVideoOn: boolean;
20
+ outputLevel: number;
21
+ sessionActive: boolean;
22
+ liveState: import("../../index.ts").LiveState;
23
+ activeTool: string | null;
24
+ status: import("../../index.ts").LiveSessionStatus;
25
+ textAvailable: boolean;
26
+ textDraft: string;
27
+ toolActive: boolean;
28
+ videoAvailable: boolean;
29
+ videoFacingMode: import("../../client/live/live-video.ts").LiveFacingMode;
30
+ videoPreview: HTMLVideoElement | null;
31
+ voiceAvailable: boolean;
32
+ canRestart: boolean;
33
+ gatePrompt: import("../../index.ts").LiveToolGatePrompt | null;
34
+ setTextDraft: import("react").Dispatch<import("react").SetStateAction<string>>;
35
+ handleEnd: () => void;
36
+ startCall: (options: {
37
+ video: boolean;
38
+ }) => void;
39
+ handleRestart: () => Promise<void>;
40
+ handleSendText: () => void;
41
+ handleToggleMic: () => void;
42
+ handleFlipCamera: () => Promise<void>;
43
+ handleToggleVideo: () => Promise<void>;
44
+ resolveGateDecision: (resolution: import("../../index.ts").ToolGateResolution) => void;
45
+ };
@@ -0,0 +1,175 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { liveIngressEnabledFromSpec } from '@theoremjs/agents';
3
+ import { liveState } from "../../client/live/live-state.js";
4
+ import { createTraceFeed } from "../../client/trace-feed.js";
5
+ import { useLiveRunnerControls } from "./use-live-runner-controls.js";
6
+ import { useLiveRunnerGate, useLiveRunnerUiState } from "./use-live-runner-ui.js";
7
+ import { useLiveSessionClient } from "./use-live-session-client.js";
8
+ /**
9
+ * Calls start from the landing (voice, or video + voice), not on mount. Once
10
+ * started, the call view stays until a reload; ending leaves Start call. Each
11
+ * restart is a new conversation; earlier calls' captions stay, above a divider.
12
+ */
13
+ function useLiveCallLifecycle(controls, captionsRef, sessionActive, videoAvailable) {
14
+ const [callStarted, setCallStarted] = useState(false);
15
+ const [ended, setEnded] = useState(false);
16
+ const videoOnConnectRef = useRef(false);
17
+ const startCall = useCallback((options) => {
18
+ videoOnConnectRef.current = options.video && videoAvailable;
19
+ setCallStarted(true);
20
+ setEnded(false);
21
+ void controls.startSession();
22
+ }, [controls, videoAvailable]);
23
+ // Camera capture needs a live session, so a video call turns it on once connected.
24
+ useEffect(() => {
25
+ if (!sessionActive || !videoOnConnectRef.current)
26
+ return;
27
+ videoOnConnectRef.current = false;
28
+ void controls.handleToggleVideo();
29
+ }, [controls, sessionActive]);
30
+ const handleEnd = useCallback(() => {
31
+ videoOnConnectRef.current = false;
32
+ controls.teardownSession();
33
+ setEnded(true);
34
+ }, [controls]);
35
+ const [pastCalls, setPastCalls] = useState([]);
36
+ const handleRestart = useCallback(async () => {
37
+ const previous = captionsRef.current.turns;
38
+ if (previous.length > 0)
39
+ setPastCalls((calls) => [...calls, previous]);
40
+ setEnded(false);
41
+ await controls.handleRestart();
42
+ }, [controls, captionsRef]);
43
+ const teardownSessionRef = useRef(controls.teardownSession);
44
+ teardownSessionRef.current = controls.teardownSession;
45
+ useEffect(() => {
46
+ return () => {
47
+ teardownSessionRef.current();
48
+ };
49
+ }, []);
50
+ return { callStarted, ended, pastCalls, startCall, handleEnd, handleRestart };
51
+ }
52
+ /**
53
+ * Owns LiveRunner state, session client, and stage callbacks.
54
+ * `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
+ */
57
+ export function useLiveRunnerModel(iface, connection) {
58
+ const ui = useLiveRunnerUiState(iface.lexicon);
59
+ const gate = useLiveRunnerGate({
60
+ setCaptions: ui.setCaptions,
61
+ setActiveTool: ui.setActiveTool,
62
+ reportFailure: ui.reportFailure,
63
+ });
64
+ const connectionRef = useRef(connection);
65
+ connectionRef.current = connection;
66
+ const voiceAvailable = liveIngressEnabledFromSpec(iface.live.ingress, 'audio');
67
+ const videoAvailable = liveIngressEnabledFromSpec(iface.live.ingress, 'video');
68
+ const textAvailable = liveIngressEnabledFromSpec(iface.live.ingress, 'text');
69
+ const state = useMemo(() => liveState({
70
+ status: ui.status,
71
+ connectPhase: ui.connectPhase,
72
+ toolName: ui.activeTool,
73
+ isMuted: ui.isMuted,
74
+ voiceEnabled: voiceAvailable,
75
+ }), [ui.activeTool, ui.connectPhase, ui.isMuted, ui.status, voiceAvailable]);
76
+ // One feed per runner: every session it opens adds its records.
77
+ const traces = useMemo(createTraceFeed, []);
78
+ const { clientRef, ensureClient, clearClient } = useLiveSessionClient({
79
+ voiceAvailable,
80
+ traces,
81
+ handleLiveTurnEvent: gate.handleLiveTurnEvent,
82
+ waitForGateDecision: gate.waitForGateDecision,
83
+ captionsRef: ui.captionsRef,
84
+ gatePromptRef: gate.gatePromptRef,
85
+ isMutedRef: ui.isMutedRef,
86
+ sessionPermissionsRef: ui.sessionPermissionsRef,
87
+ videoCaptureRef: ui.videoCaptureRef,
88
+ setConnectPhase: ui.setConnectPhase,
89
+ setStatus: ui.setStatus,
90
+ setSessionActive: ui.setSessionActive,
91
+ reportFailure: ui.reportFailure,
92
+ clearFailure: ui.clearFailure,
93
+ reportSessionEnded: ui.reportSessionEnded,
94
+ clearSessionEnded: ui.clearSessionEnded,
95
+ setCaptions: ui.setCaptions,
96
+ setInputLevel: ui.setInputLevel,
97
+ setOutputLevel: ui.setOutputLevel,
98
+ setActiveTool: ui.setActiveTool,
99
+ setSessionPermissions: ui.setSessionPermissions,
100
+ setVideoPreview: ui.setVideoPreview,
101
+ setVideoFacingMode: ui.setVideoFacingMode,
102
+ setIsVideoOn: ui.setIsVideoOn,
103
+ });
104
+ const controls = useLiveRunnerControls({
105
+ clientRef,
106
+ videoCaptureRef: ui.videoCaptureRef,
107
+ captionsRef: ui.captionsRef,
108
+ connectionRef,
109
+ statusRef: ui.statusRef,
110
+ isMutedRef: ui.isMutedRef,
111
+ sessionPermissionsRef: ui.sessionPermissionsRef,
112
+ ensureClient,
113
+ clearClient,
114
+ cancelGateDecision: gate.cancelGateDecision,
115
+ stopVideo: ui.stopVideo,
116
+ resetCaptions: ui.resetCaptions,
117
+ sessionActive: ui.sessionActive,
118
+ textAvailable,
119
+ videoAvailable,
120
+ voiceAvailable,
121
+ isVideoOn: ui.isVideoOn,
122
+ textDraft: ui.textDraft,
123
+ setTextDraft: ui.setTextDraft,
124
+ setCaptions: ui.setCaptions,
125
+ reportFailure: ui.reportFailure,
126
+ clearFailure: ui.clearFailure,
127
+ setIsMuted: ui.setIsMuted,
128
+ setSessionActive: ui.setSessionActive,
129
+ setSessionPermissions: ui.setSessionPermissions,
130
+ setStatus: ui.setStatus,
131
+ setConnectPhase: ui.setConnectPhase,
132
+ setInputLevel: ui.setInputLevel,
133
+ setOutputLevel: ui.setOutputLevel,
134
+ setVideoPreview: ui.setVideoPreview,
135
+ setVideoFacingMode: ui.setVideoFacingMode,
136
+ setIsVideoOn: ui.setIsVideoOn,
137
+ });
138
+ const { callStarted, ended, pastCalls, startCall, handleEnd, handleRestart } = useLiveCallLifecycle(controls, ui.captionsRef, ui.sessionActive, videoAvailable);
139
+ return {
140
+ handle: iface.identity.handle,
141
+ traces,
142
+ callStarted,
143
+ pastCalls,
144
+ captions: ui.captions,
145
+ failure: ui.failure,
146
+ sessionEnded: ui.sessionEnded,
147
+ inputLevel: ui.inputLevel,
148
+ isMuted: ui.isMuted,
149
+ isVideoOn: ui.isVideoOn,
150
+ outputLevel: ui.outputLevel,
151
+ sessionActive: ui.sessionActive,
152
+ liveState: state,
153
+ activeTool: ui.activeTool,
154
+ status: ui.status,
155
+ textAvailable,
156
+ textDraft: ui.textDraft,
157
+ toolActive: ui.activeTool !== null || gate.gatePrompt !== null,
158
+ videoAvailable,
159
+ videoFacingMode: ui.videoFacingMode,
160
+ videoPreview: ui.videoPreview,
161
+ 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),
164
+ gatePrompt: gate.gatePrompt,
165
+ setTextDraft: ui.setTextDraft,
166
+ handleEnd,
167
+ startCall,
168
+ handleRestart,
169
+ handleSendText: controls.handleSendText,
170
+ handleToggleMic: controls.handleToggleMic,
171
+ handleFlipCamera: controls.handleFlipCamera,
172
+ handleToggleVideo: controls.handleToggleVideo,
173
+ resolveGateDecision: gate.resolveGateDecision,
174
+ };
175
+ }
@@ -0,0 +1,65 @@
1
+ import { type Dispatch, type SetStateAction } from 'react';
2
+ import { type LexiconOverrides, type SessionEventOf } from '@theoremjs/agents';
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 { LiveGateAnswer, LiveToolGatePrompt } from '../../client/live/live-tool.ts';
7
+ import type { LiveFacingMode, LiveVideoCapture } from '../../client/live/live-video.ts';
8
+ import type { LiveConnectPhase, LiveSessionStatus } from '../../client/live/live-state.ts';
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): {
12
+ status: LiveSessionStatus;
13
+ setStatus: Dispatch<SetStateAction<LiveSessionStatus>>;
14
+ connectPhase: LiveConnectPhase | null;
15
+ setConnectPhase: Dispatch<SetStateAction<LiveConnectPhase | null>>;
16
+ inputLevel: number;
17
+ setInputLevel: Dispatch<SetStateAction<number>>;
18
+ outputLevel: number;
19
+ setOutputLevel: Dispatch<SetStateAction<number>>;
20
+ isMuted: boolean;
21
+ setIsMuted: Dispatch<SetStateAction<boolean>>;
22
+ isVideoOn: boolean;
23
+ setIsVideoOn: Dispatch<SetStateAction<boolean>>;
24
+ activeTool: string | null;
25
+ setActiveTool: Dispatch<SetStateAction<string | null>>;
26
+ captions: LiveCaptionState;
27
+ setCaptions: Dispatch<SetStateAction<LiveCaptionState>>;
28
+ failure: ClientFailure | null;
29
+ reportFailure: (err: unknown) => void;
30
+ clearFailure: () => void;
31
+ sessionEnded: string | null;
32
+ reportSessionEnded: (session: SessionEventOf<"ended">) => void;
33
+ clearSessionEnded: () => void;
34
+ textDraft: string;
35
+ setTextDraft: Dispatch<SetStateAction<string>>;
36
+ sessionActive: boolean;
37
+ setSessionActive: Dispatch<SetStateAction<boolean>>;
38
+ sessionPermissions: string[];
39
+ setSessionPermissions: Dispatch<SetStateAction<string[]>>;
40
+ everConnected: boolean;
41
+ videoPreview: HTMLVideoElement | null;
42
+ setVideoPreview: Dispatch<SetStateAction<HTMLVideoElement | null>>;
43
+ videoFacingMode: LiveFacingMode;
44
+ setVideoFacingMode: Dispatch<SetStateAction<LiveFacingMode>>;
45
+ videoCaptureRef: import("react").RefObject<LiveVideoCapture | null>;
46
+ captionsRef: import("react").RefObject<LiveCaptionState>;
47
+ isMutedRef: import("react").RefObject<boolean>;
48
+ sessionPermissionsRef: import("react").RefObject<string[]>;
49
+ statusRef: import("react").RefObject<LiveSessionStatus>;
50
+ resetCaptions: () => void;
51
+ stopVideo: () => void;
52
+ };
53
+ /** Gate prompt + live turn-tool event wiring. */
54
+ export declare function useLiveRunnerGate(args: {
55
+ setCaptions: Dispatch<SetStateAction<LiveCaptionState>>;
56
+ setActiveTool: Dispatch<SetStateAction<string | null>>;
57
+ reportFailure: (err: unknown) => void;
58
+ }): {
59
+ gatePrompt: LiveToolGatePrompt | null;
60
+ gatePromptRef: import("react").RefObject<LiveToolGatePrompt | null>;
61
+ handleLiveTurnEvent: (event: Parameters<typeof applyLiveTurnToolEvent>[0]) => void;
62
+ waitForGateDecision: (prompt: LiveToolGatePrompt) => Promise<LiveGateAnswer>;
63
+ resolveGateDecision: (resolution: ToolGateResolution) => void;
64
+ cancelGateDecision: () => void;
65
+ };
@@ -0,0 +1,159 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { TheoremError } from '@theoremjs/agents';
3
+ import { clientFailure } from "../../client/failure.js";
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) {
8
+ const [status, setStatus] = useState('disconnected');
9
+ const [connectPhase, setConnectPhase] = useState(null);
10
+ const [inputLevel, setInputLevel] = useState(0);
11
+ const [outputLevel, setOutputLevel] = useState(0);
12
+ const [isMuted, setIsMuted] = useState(false);
13
+ const [isVideoOn, setIsVideoOn] = useState(false);
14
+ const [activeTool, setActiveTool] = useState(null);
15
+ const [captions, setCaptions] = useState(emptyLiveCaptionState);
16
+ const [failure, setFailure] = useState(null);
17
+ /** The user's line for a session the provider ended after warning it would. */
18
+ const [sessionEnded, setSessionEnded] = useState(null);
19
+ const [textDraft, setTextDraft] = useState('');
20
+ const [sessionActive, setSessionActive] = useState(false);
21
+ const [sessionPermissions, setSessionPermissions] = useState([]);
22
+ const [everConnected, setEverConnected] = useState(false);
23
+ const [videoPreview, setVideoPreview] = useState(null);
24
+ const [videoFacingMode, setVideoFacingMode] = useState('user');
25
+ const videoCaptureRef = useRef(null);
26
+ const captionsRef = useRef(captions);
27
+ const isMutedRef = useRef(isMuted);
28
+ const sessionPermissionsRef = useRef(sessionPermissions);
29
+ const statusRef = useRef(status);
30
+ captionsRef.current = captions;
31
+ isMutedRef.current = isMuted;
32
+ sessionPermissionsRef.current = sessionPermissions;
33
+ statusRef.current = status;
34
+ useEffect(() => {
35
+ if (sessionActive)
36
+ setEverConnected(true);
37
+ }, [sessionActive]);
38
+ const reportFailure = useCallback((err) => {
39
+ setFailure(clientFailure(err, lexicon));
40
+ }, [lexicon]);
41
+ const clearFailure = useCallback(() => {
42
+ setFailure(null);
43
+ }, []);
44
+ const reportSessionEnded = useCallback((session) => {
45
+ setSessionEnded(session.message);
46
+ }, []);
47
+ const clearSessionEnded = useCallback(() => {
48
+ setSessionEnded(null);
49
+ }, []);
50
+ const resetCaptions = useCallback(() => {
51
+ setCaptions(emptyLiveCaptionState());
52
+ }, []);
53
+ const stopVideo = useCallback(() => {
54
+ if (videoCaptureRef.current) {
55
+ videoCaptureRef.current.stop();
56
+ videoCaptureRef.current = null;
57
+ }
58
+ setVideoPreview(null);
59
+ setVideoFacingMode('user');
60
+ setIsVideoOn(false);
61
+ }, []);
62
+ return {
63
+ status,
64
+ setStatus,
65
+ connectPhase,
66
+ setConnectPhase,
67
+ inputLevel,
68
+ setInputLevel,
69
+ outputLevel,
70
+ setOutputLevel,
71
+ isMuted,
72
+ setIsMuted,
73
+ isVideoOn,
74
+ setIsVideoOn,
75
+ activeTool,
76
+ setActiveTool,
77
+ captions,
78
+ setCaptions,
79
+ failure,
80
+ reportFailure,
81
+ clearFailure,
82
+ sessionEnded,
83
+ reportSessionEnded,
84
+ clearSessionEnded,
85
+ textDraft,
86
+ setTextDraft,
87
+ sessionActive,
88
+ setSessionActive,
89
+ sessionPermissions,
90
+ setSessionPermissions,
91
+ everConnected,
92
+ videoPreview,
93
+ setVideoPreview,
94
+ videoFacingMode,
95
+ setVideoFacingMode,
96
+ videoCaptureRef,
97
+ captionsRef,
98
+ isMutedRef,
99
+ sessionPermissionsRef,
100
+ statusRef,
101
+ resetCaptions,
102
+ stopVideo,
103
+ };
104
+ }
105
+ /** Gate prompt + live turn-tool event wiring. */
106
+ export function useLiveRunnerGate(args) {
107
+ const [gatePrompt, setGatePrompt] = useState(null);
108
+ const gatePromptRef = useRef(gatePrompt);
109
+ gatePromptRef.current = gatePrompt;
110
+ const gateResolverRef = useRef(null);
111
+ const gateRejectRef = useRef(null);
112
+ const answerGate = useCallback((answer) => {
113
+ gateResolverRef.current?.(answer);
114
+ gateResolverRef.current = null;
115
+ gateRejectRef.current = null;
116
+ setGatePrompt(null);
117
+ }, []);
118
+ const handleLiveTurnEvent = useCallback((event) => {
119
+ applyLiveTurnToolEvent(event, {
120
+ gateCallId: gatePromptRef.current?.callId,
121
+ withdrawGate: () => {
122
+ answerGate('withdrawn');
123
+ },
124
+ clearInterim: () => {
125
+ args.setCaptions((prev) => clearLiveCaptionInterim(prev));
126
+ },
127
+ clearActiveTool: () => {
128
+ args.setActiveTool(null);
129
+ },
130
+ reportFailure: args.reportFailure,
131
+ setActiveTool: args.setActiveTool,
132
+ });
133
+ }, [args, answerGate]);
134
+ const waitForGateDecision = useCallback((prompt) => {
135
+ return new Promise((resolve, reject) => {
136
+ setGatePrompt(prompt);
137
+ gateResolverRef.current = resolve;
138
+ gateRejectRef.current = reject;
139
+ });
140
+ }, []);
141
+ const resolveGateDecision = useCallback((resolution) => {
142
+ answerGate(resolution);
143
+ }, [answerGate]);
144
+ const cancelGateDecision = useCallback(() => {
145
+ // lexicon-exempt: internal diagnostic; the user reads error.cancelled
146
+ gateRejectRef.current?.(new TheoremError('cancelled', 'live session ended with a gate open'));
147
+ gateResolverRef.current = null;
148
+ gateRejectRef.current = null;
149
+ setGatePrompt(null);
150
+ }, []);
151
+ return {
152
+ gatePrompt,
153
+ gatePromptRef,
154
+ handleLiveTurnEvent,
155
+ waitForGateDecision,
156
+ resolveGateDecision,
157
+ cancelGateDecision,
158
+ };
159
+ }
@@ -0,0 +1,41 @@
1
+ import { type Dispatch, type MutableRefObject, type SetStateAction } from 'react';
2
+ import { type SessionEventOf, type TurnEvent } from '@theoremjs/agents';
3
+ import { type LiveCaptionState } from '../../client/live/live-captions.ts';
4
+ import type { LiveGateAnswer, LiveToolGatePrompt } from '../../client/live/live-tool.ts';
5
+ import type { LiveFacingMode, LiveVideoCapture } from '../../client/live/live-video.ts';
6
+ import type { LiveConnectPhase, LiveSessionStatus } from '../../client/live/live-state.ts';
7
+ import { type LiveConnection, LiveSessionClient } from '../../client/live-client.ts';
8
+ import type { TraceFeed } from '../../client/trace-feed.ts';
9
+ export type LiveClientBindings = {
10
+ voiceAvailable: boolean;
11
+ /** Where the session's trace records go, when the relay delivers them. */
12
+ traces: TraceFeed;
13
+ handleLiveTurnEvent: (event: TurnEvent) => void;
14
+ waitForGateDecision: (prompt: LiveToolGatePrompt) => Promise<LiveGateAnswer>;
15
+ captionsRef: MutableRefObject<LiveCaptionState>;
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,123 @@
1
+ import { useCallback, useRef } from 'react';
2
+ import { TheoremError } from '@theoremjs/agents';
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
+ const next = applyLiveTranscript(bindings.captionsRef.current, text, isUser, meta?.interim);
27
+ bindings.captionsRef.current = next;
28
+ bindings.setCaptions(next);
29
+ }
30
+ async function onLiveToolCall(name, toolArgs, meta, bindings, clientRef) {
31
+ bindings.setActiveTool(name);
32
+ bindings.clearFailure();
33
+ const client = clientRef.current;
34
+ if (!client) {
35
+ // lexicon-exempt: internal diagnostic; the user reads error.request
36
+ bindings.reportFailure(new TheoremError('request', 'live tool call with no session client'));
37
+ return;
38
+ }
39
+ try {
40
+ await runLiveToolCall({
41
+ executeToolOnRelay: (call) => client.executeToolOnRelay(call),
42
+ name,
43
+ toolArgs,
44
+ callId: meta.callId,
45
+ sessionPermissions: bindings.sessionPermissionsRef.current,
46
+ setSessionPermissions: (next) => {
47
+ bindings.sessionPermissionsRef.current = next;
48
+ bindings.setSessionPermissions(next);
49
+ },
50
+ waitForGateDecision: bindings.waitForGateDecision,
51
+ });
52
+ }
53
+ catch (err) {
54
+ bindings.reportFailure(err);
55
+ }
56
+ finally {
57
+ if (!bindings.gatePromptRef.current)
58
+ bindings.setActiveTool(null);
59
+ }
60
+ }
61
+ /** Owns LiveSessionClient construction + reconnect identity. */
62
+ export function useLiveSessionClient(bindings) {
63
+ const clientRef = useRef(null);
64
+ const bindingsRef = useRef(bindings);
65
+ bindingsRef.current = bindings;
66
+ const ensureClient = useCallback((connection) => {
67
+ if (clientRef.current)
68
+ return clientRef.current;
69
+ const b = bindingsRef.current;
70
+ const client = new LiveSessionClient({
71
+ ...connection,
72
+ voiceIngress: b.voiceAvailable,
73
+ onConnectPhase: (phase) => {
74
+ b.setConnectPhase(phase);
75
+ },
76
+ onStatusChange: (next) => {
77
+ onLiveStatusChange(next, bindingsRef.current);
78
+ },
79
+ onTranscript: (text, isUser, meta) => {
80
+ onLiveTranscript(text, isUser, meta, bindingsRef.current);
81
+ },
82
+ onTurnEvent: (event) => {
83
+ // An `unsupported` event is the host's to read; Theorem's UI shows nothing for it.
84
+ if (event.type === 'unsupported')
85
+ return;
86
+ // A `malformed` one was left out and the call goes on; the user reads that part was skipped.
87
+ if (event.type === 'malformed') {
88
+ bindingsRef.current.reportFailure(event.error);
89
+ return;
90
+ }
91
+ bindingsRef.current.handleLiveTurnEvent(event);
92
+ },
93
+ onTrace: (record) => {
94
+ bindingsRef.current.traces.push(record);
95
+ },
96
+ onVolumeLevel: (level, isUser) => {
97
+ const current = bindingsRef.current;
98
+ if (isUser) {
99
+ current.setInputLevel(current.isMutedRef.current ? 0 : level);
100
+ }
101
+ else {
102
+ current.setOutputLevel(level);
103
+ }
104
+ },
105
+ onError: (err) => {
106
+ bindingsRef.current.reportFailure(err);
107
+ },
108
+ onSessionEnded: (session) => {
109
+ bindingsRef.current.reportSessionEnded(session);
110
+ },
111
+ onToolCall: (name, args, meta) => onLiveToolCall(name, args, meta, bindingsRef.current, clientRef),
112
+ });
113
+ clientRef.current = client;
114
+ return client;
115
+ }, []);
116
+ const clearClient = useCallback(() => {
117
+ if (clientRef.current) {
118
+ clientRef.current.disconnect();
119
+ clientRef.current = null;
120
+ }
121
+ }, []);
122
+ return { clientRef, ensureClient, clearClient };
123
+ }
@@ -0,0 +1,28 @@
1
+ import { type LexiconOverrides } 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 lexicon's `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 `lexicon`: the profile's wording for voice failures. */
16
+ lexicon: LexiconOverrides;
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
+ };