@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,287 @@
1
+ import { TheoremError } from '@theoremjs/agents';
2
+ import { convertSteersToFrontQueued, createComposerPendingMessage, orderComposerPendingMessages, removeComposerPendingMessage, userDraftHasPayload, userDraftToSteerInject, } from '@theoremjs/agents/interface';
3
+ import { useCallback } from 'react';
4
+ import { clientFailure } from "../client/failure.js";
5
+ import { composerFieldsFromDraft, encodeComposerDraft, resumeInterfaceTool, streamInterfaceDraftTurn, streamInterfaceTurn, } from "../client/index.js";
6
+ import { issuedOperationId } from "../client/turn-client.js";
7
+ function composerFieldsPayload(text, pendingFiles, pendingVoice) {
8
+ const meta = (files) => files.map((f) => ({
9
+ name: f.name,
10
+ mimeType: f.type || 'application/octet-stream',
11
+ sizeBytes: f.size,
12
+ }));
13
+ return {
14
+ ...(text.trim() ? { text } : {}),
15
+ ...(pendingFiles.length ? { attachments: meta(pendingFiles) } : {}),
16
+ ...(pendingVoice.length ? { voice: meta(pendingVoice) } : {}),
17
+ };
18
+ }
19
+ function newTurnId() {
20
+ return typeof crypto !== 'undefined' && 'randomUUID' in crypto
21
+ ? crypto.randomUUID()
22
+ : `turn-${Date.now()}`;
23
+ }
24
+ function beginAbortableTurn(args) {
25
+ if (!args.iface)
26
+ return null;
27
+ const controller = new AbortController();
28
+ args.abortRef.current = controller;
29
+ const turnId = newTurnId();
30
+ args.turnIdRef.current = turnId;
31
+ return {
32
+ composer: args.iface,
33
+ turnId,
34
+ signal: controller.signal,
35
+ };
36
+ }
37
+ /** The run's scope: the conversation the chat continues, and the run's own turn. */
38
+ function scopeOf(args, turnId) {
39
+ return { conversation: args.conversationRef.current, turnId };
40
+ }
41
+ /**
42
+ * Puts a message back in the composer. What the composer holds now is
43
+ * stashed first, so nothing the user typed is lost.
44
+ */
45
+ function useRestoreToComposer(args) {
46
+ return useCallback(async (fields) => {
47
+ const { draftText, pendingFiles, pendingVoice } = args.composerRef.current;
48
+ if (userDraftHasPayload(composerFieldsPayload(draftText, pendingFiles, pendingVoice))) {
49
+ const draft = await encodeComposerDraft({ text: draftText, pendingFiles, pendingVoice });
50
+ const stash = createComposerPendingMessage({ kind: 'stash', draft });
51
+ args.setPendingMessages((prev) => orderComposerPendingMessages([...prev, stash]));
52
+ }
53
+ args.setDraftText(fields.text);
54
+ args.setPendingFiles(fields.files);
55
+ args.setPendingVoice(fields.voice);
56
+ args.setIssues([]);
57
+ }, [args]);
58
+ }
59
+ /** Starts a turn from the composer's fields, or from an encoded draft (queued or send-now). */
60
+ function useTurnStarters(args, restoreToComposer) {
61
+ const startTurnFromFields = useCallback(async (fields) => {
62
+ const started = beginAbortableTurn(args);
63
+ if (!started)
64
+ return;
65
+ await args.runTurnStream((view) => streamInterfaceTurn({
66
+ iface: started.composer,
67
+ transport: args.transport,
68
+ session: args.sessionRef.current,
69
+ text: fields.text,
70
+ pendingFiles: fields.files,
71
+ pendingVoice: fields.voice,
72
+ signal: started.signal,
73
+ scope: scopeOf(args, started.turnId),
74
+ view,
75
+ transcript: args.blocksRef.current,
76
+ onUserBlocks: (userBlocks) => {
77
+ args.setBlocks((prev) => [...prev, ...userBlocks]);
78
+ args.setDelivery({ status: 'sending' });
79
+ args.setChatStarted(true);
80
+ args.setStreaming(true);
81
+ args.clearComposer();
82
+ },
83
+ }), { message: { restore: () => restoreToComposer(fields) } });
84
+ }, [args, restoreToComposer]);
85
+ const startTurnFromDraft = useCallback(async (draft, options = {}) => {
86
+ const started = beginAbortableTurn(args);
87
+ if (!started)
88
+ return;
89
+ await args.runTurnStream((view) => streamInterfaceDraftTurn({
90
+ iface: started.composer,
91
+ transport: args.transport,
92
+ session: args.sessionRef.current,
93
+ draft,
94
+ signal: started.signal,
95
+ scope: scopeOf(args, started.turnId),
96
+ view,
97
+ transcript: args.blocksRef.current,
98
+ walkAway: options.walkAway === true,
99
+ onUserBlocks: (posted) => {
100
+ args.setBlocks((prev) => [...prev, ...posted]);
101
+ args.setStreamBlocks([]);
102
+ args.setDelivery({ status: 'sending' });
103
+ args.setChatStarted(true);
104
+ args.setStreaming(true);
105
+ options.onPosted?.();
106
+ },
107
+ }), { message: { restore: () => restoreToComposer(composerFieldsFromDraft(draft)) } });
108
+ }, [args, restoreToComposer]);
109
+ return { startTurnFromFields, startTurnFromDraft };
110
+ }
111
+ /** A steer with no running turn: the user reads `session.turn_ended`; queued steers move to the front. */
112
+ function steerAfterTurnEnded(args, messageId) {
113
+ args.setFailure(clientFailure(
114
+ // labels-exempt: internal diagnostic; the user reads session.turn_ended
115
+ new TheoremError('request', 'steer: no active turn', { copy: { key: 'session.turn_ended' } }), args.iface?.labels));
116
+ args.setPendingMessages((prev) => orderComposerPendingMessages(convertSteersToFrontQueued(removeComposerPendingMessage(prev, messageId))));
117
+ }
118
+ /** Queue / steer / stash the composer draft, and restore a pending message into the composer. */
119
+ function usePendingActions(args, restoreToComposer) {
120
+ const enqueuePending = useCallback(async (kind) => {
121
+ if (!userDraftHasPayload(composerFieldsPayload(args.draftText, args.pendingFiles, args.pendingVoice))) {
122
+ return;
123
+ }
124
+ args.setIssues([]);
125
+ try {
126
+ const draft = await encodeComposerDraft({
127
+ text: args.draftText,
128
+ pendingFiles: args.pendingFiles,
129
+ pendingVoice: args.pendingVoice,
130
+ });
131
+ const message = createComposerPendingMessage({ kind, draft });
132
+ args.setPendingMessages((prev) => orderComposerPendingMessages([...prev, message]));
133
+ args.clearComposer();
134
+ if (kind !== 'steer')
135
+ return;
136
+ const turnId = args.turnIdRef.current;
137
+ if (!turnId) {
138
+ steerAfterTurnEnded(args, message.id);
139
+ return;
140
+ }
141
+ const inject = userDraftToSteerInject(draft);
142
+ if (inject.length === 0)
143
+ return;
144
+ await args.transport.steer({ turnId, id: message.id, inject });
145
+ }
146
+ catch (err) {
147
+ args.setFailure(clientFailure(err, args.iface?.labels));
148
+ }
149
+ }, [args]);
150
+ const handlePendingRestore = useCallback(async (id) => {
151
+ const message = args.pendingRef.current.find((m) => m.id === id);
152
+ if (!message)
153
+ return;
154
+ try {
155
+ await restoreToComposer(composerFieldsFromDraft(message.draft));
156
+ args.setPendingMessages((prev) => removeComposerPendingMessage(prev, id));
157
+ }
158
+ catch (err) {
159
+ args.setFailure(clientFailure(err, args.iface?.labels));
160
+ }
161
+ }, [args, restoreToComposer]);
162
+ return { enqueuePending, handlePendingRestore };
163
+ }
164
+ /** Answer the gate the session waits on: a decision, or a sign-in. */
165
+ function useGateActions(args) {
166
+ const resumeGatedTool = useCallback(async (resolution) => {
167
+ const composer = args.iface;
168
+ if (!composer)
169
+ return;
170
+ // why: Set once the run starts (not while another runs), and cleared once it has ended.
171
+ let started = false;
172
+ try {
173
+ await args.runTurnStream((view) => {
174
+ started = true;
175
+ const session = args.sessionRef.current;
176
+ // why: A person's answer shows on its gate while it settles; the page's has no gate to show on.
177
+ if (session.gatedTool && resolution.action !== 'page')
178
+ args.setAnswering({
179
+ callId: session.gatedTool.callId,
180
+ operationId: issuedOperationId(session.gatedTool),
181
+ action: resolution.action,
182
+ });
183
+ return resumeInterfaceTool({
184
+ iface: composer,
185
+ transport: args.transport,
186
+ session,
187
+ scope: scopeOf(args),
188
+ resolution,
189
+ view,
190
+ });
191
+ });
192
+ }
193
+ finally {
194
+ if (started)
195
+ args.setAnswering(null);
196
+ }
197
+ }, [args]);
198
+ const handleToolDecision = useCallback(async (_index, action) => {
199
+ await resumeGatedTool({ action });
200
+ }, [resumeGatedTool]);
201
+ const handleAuthenticated = useCallback(async (_index, secret) => {
202
+ await resumeGatedTool({ action: 'auth', ...(secret === undefined ? {} : { secret }) });
203
+ }, [resumeGatedTool]);
204
+ return { handleToolDecision, handleAuthenticated, resumeGatedTool };
205
+ }
206
+ export function useTheoremChatActions(args) {
207
+ const restoreToComposer = useRestoreToComposer(args);
208
+ const { startTurnFromFields, startTurnFromDraft } = useTurnStarters(args, restoreToComposer);
209
+ const { enqueuePending, handlePendingRestore } = usePendingActions(args, restoreToComposer);
210
+ const { handleToolDecision, handleAuthenticated, resumeGatedTool } = useGateActions(args);
211
+ const handleStop = useCallback(() => {
212
+ args.abortRef.current?.abort();
213
+ }, [args]);
214
+ const handleSubmit = useCallback(async () => {
215
+ if (args.phase === 'streaming' || args.phase === 'gated') {
216
+ await enqueuePending('queue');
217
+ return;
218
+ }
219
+ if (args.gated)
220
+ return;
221
+ args.setIssues([]);
222
+ await startTurnFromFields({
223
+ text: args.draftText,
224
+ files: [...args.pendingFiles],
225
+ voice: [...args.pendingVoice],
226
+ });
227
+ }, [args, enqueuePending, startTurnFromFields]);
228
+ /** The queued message's draft, or the composer's when it holds something to send. */
229
+ const draftToSend = useCallback(async (draftSource) => {
230
+ if (draftSource)
231
+ return draftSource.draft;
232
+ const draft = await encodeComposerDraft({
233
+ text: args.draftText,
234
+ pendingFiles: args.pendingFiles,
235
+ pendingVoice: args.pendingVoice,
236
+ });
237
+ return userDraftHasPayload(draft) ? draft : undefined;
238
+ }, [args]);
239
+ /** The sent message leaves the queue, or the composer it came from. */
240
+ const releaseDraft = useCallback((draftSource) => {
241
+ if (!draftSource) {
242
+ args.clearComposer();
243
+ return;
244
+ }
245
+ const pendingId = draftSource.id;
246
+ args.setPendingMessages((prev) => removeComposerPendingMessage(prev, pendingId));
247
+ }, [args]);
248
+ const handleSendNow = useCallback(async (draftSource) => {
249
+ if (!args.iface)
250
+ return;
251
+ const draft = await draftToSend(draftSource);
252
+ if (!draft)
253
+ return;
254
+ if (args.busyRef.current) {
255
+ args.abortRef.current?.abort();
256
+ await args.runPromiseRef.current;
257
+ }
258
+ args.setPendingMessages((prev) => convertSteersToFrontQueued(prev));
259
+ args.allowQueueDrainRef.current = false;
260
+ // why: Sent while the reply waits on gates, the message walks away from them in its own request.
261
+ await startTurnFromDraft(draft, {
262
+ walkAway: args.sessionRef.current.gatedTool !== null,
263
+ onPosted: () => releaseDraft(draftSource),
264
+ });
265
+ }, [args, draftToSend, releaseDraft, startTurnFromDraft]);
266
+ const handleMenuAction = useCallback((action) => {
267
+ if (action === 'queue' || action === 'steer' || action === 'stash') {
268
+ void enqueuePending(action);
269
+ return;
270
+ }
271
+ if (action === 'send_now')
272
+ void handleSendNow();
273
+ }, [enqueuePending, handleSendNow]);
274
+ return {
275
+ startTurnFromFields,
276
+ startTurnFromDraft,
277
+ handleStop,
278
+ handleSubmit,
279
+ handleSendNow,
280
+ handleMenuAction,
281
+ handleToolDecision,
282
+ handleAuthenticated,
283
+ resumeGatedTool,
284
+ handlePendingRestore,
285
+ enqueuePending,
286
+ };
287
+ }
@@ -0,0 +1,66 @@
1
+ import { type AttachmentValidationIssue, type ComposerPendingMessage, type InterfaceTurnSession, type TranscriptBlock } from '@theoremjs/agents/interface';
2
+ import type { ClientFailure } from '../client/failure.ts';
3
+ import type { AnsweringGate } from '../client/tool-resume.ts';
4
+ type SetSession = (value: InterfaceTurnSession | ((prev: InterfaceTurnSession) => InterfaceTurnSession)) => void;
5
+ export type { SetSession };
6
+ /**
7
+ * How far the latest message has got: posted (`sending`), taken by the server
8
+ * (`sent`), reached the model (`delivered`), answered (`read`).
9
+ */
10
+ export type MessageDelivery = 'sending' | 'sent' | 'delivered' | 'read';
11
+ /** Composer / transcript / session state behind {@link useTheoremChat}. */
12
+ export declare function useTheoremChatState(initialText?: string): {
13
+ blocks: TranscriptBlock[];
14
+ setBlocks: (value: TranscriptBlock[] | ((prev: TranscriptBlock[]) => TranscriptBlock[])) => void;
15
+ streamBlocks: TranscriptBlock[];
16
+ setStreamBlocks: import("react").Dispatch<import("react").SetStateAction<TranscriptBlock[]>>;
17
+ streamBlocksRef: import("react").RefObject<TranscriptBlock[]>;
18
+ draftText: string;
19
+ setDraftText: import("react").Dispatch<import("react").SetStateAction<string>>;
20
+ pendingFiles: File[];
21
+ setPendingFiles: import("react").Dispatch<import("react").SetStateAction<File[]>>;
22
+ pendingVoice: File[];
23
+ setPendingVoice: import("react").Dispatch<import("react").SetStateAction<File[]>>;
24
+ pendingMessages: ComposerPendingMessage[];
25
+ setPendingMessages: import("react").Dispatch<import("react").SetStateAction<ComposerPendingMessage[]>>;
26
+ issues: AttachmentValidationIssue[];
27
+ setIssues: import("react").Dispatch<import("react").SetStateAction<AttachmentValidationIssue[]>>;
28
+ failure: ClientFailure | null;
29
+ setFailure: import("react").Dispatch<import("react").SetStateAction<ClientFailure | null>>;
30
+ answering: AnsweringGate | null;
31
+ setAnswering: import("react").Dispatch<import("react").SetStateAction<AnsweringGate | null>>;
32
+ busy: boolean;
33
+ setBusy: import("react").Dispatch<import("react").SetStateAction<boolean>>;
34
+ chatStarted: boolean;
35
+ setChatStarted: import("react").Dispatch<import("react").SetStateAction<boolean>>;
36
+ streaming: boolean;
37
+ setStreaming: import("react").Dispatch<import("react").SetStateAction<boolean>>;
38
+ session: InterfaceTurnSession;
39
+ setSession: import("react").Dispatch<import("react").SetStateAction<InterfaceTurnSession>>;
40
+ delivery: {
41
+ status: MessageDelivery;
42
+ } | null;
43
+ setDelivery: (next: {
44
+ status: MessageDelivery;
45
+ } | null) => void;
46
+ deliveryRef: import("react").RefObject<{
47
+ status: MessageDelivery;
48
+ } | null>;
49
+ composerRef: import("react").RefObject<{
50
+ draftText: string;
51
+ pendingFiles: File[];
52
+ pendingVoice: File[];
53
+ }>;
54
+ blocksRef: import("react").RefObject<TranscriptBlock[]>;
55
+ busyRef: import("react").RefObject<boolean>;
56
+ abortRef: import("react").RefObject<AbortController | null>;
57
+ turnIdRef: import("react").RefObject<string | null>;
58
+ pendingRef: import("react").RefObject<ComposerPendingMessage[]>;
59
+ sessionRef: import("react").RefObject<InterfaceTurnSession>;
60
+ drainLockRef: import("react").RefObject<boolean>;
61
+ allowQueueDrainRef: import("react").RefObject<boolean>;
62
+ runPromiseRef: import("react").RefObject<Promise<void> | null>;
63
+ cancelPendingStreamFrame: () => void;
64
+ scheduleStreamBlocks: (partial: TranscriptBlock[]) => void;
65
+ clearComposer: () => void;
66
+ };
@@ -0,0 +1,122 @@
1
+ import { emptyInterfaceTurnSession, } from '@theoremjs/agents/interface';
2
+ import { useCallback, useRef, useState } from 'react';
3
+ /** Coalesces streamed partial blocks to one state update per animation frame. */
4
+ function useStreamFrames(setStreamBlocks) {
5
+ const streamRafRef = useRef(null);
6
+ const pendingStreamRef = useRef(null);
7
+ const cancelPendingStreamFrame = useCallback(() => {
8
+ if (streamRafRef.current != null) {
9
+ cancelAnimationFrame(streamRafRef.current);
10
+ streamRafRef.current = null;
11
+ }
12
+ pendingStreamRef.current = null;
13
+ }, []);
14
+ const scheduleStreamBlocks = useCallback((partial) => {
15
+ pendingStreamRef.current = partial;
16
+ if (streamRafRef.current != null)
17
+ return;
18
+ streamRafRef.current = requestAnimationFrame(() => {
19
+ streamRafRef.current = null;
20
+ const next = pendingStreamRef.current;
21
+ pendingStreamRef.current = null;
22
+ if (next)
23
+ setStreamBlocks(next);
24
+ });
25
+ }, [setStreamBlocks]);
26
+ return { cancelPendingStreamFrame, scheduleStreamBlocks };
27
+ }
28
+ /** Composer / transcript / session state behind {@link useTheoremChat}. */
29
+ export function useTheoremChatState(initialText = '') {
30
+ const [blocks, setBlocksState] = useState([]);
31
+ const [streamBlocks, setStreamBlocks] = useState([]);
32
+ const [draftText, setDraftText] = useState(initialText);
33
+ const [pendingFiles, setPendingFiles] = useState([]);
34
+ const [pendingVoice, setPendingVoice] = useState([]);
35
+ const [pendingMessages, setPendingMessages] = useState([]);
36
+ const [issues, setIssues] = useState([]);
37
+ const [failure, setFailure] = useState(null);
38
+ const [answering, setAnswering] = useState(null);
39
+ const [busy, setBusy] = useState(false);
40
+ const [chatStarted, setChatStarted] = useState(false);
41
+ const [streaming, setStreaming] = useState(false);
42
+ const [session, setSession] = useState(emptyInterfaceTurnSession);
43
+ // why: One object per posted message, so a run can tell whether it posted one.
44
+ const [delivery, setDeliveryState] = useState(null);
45
+ const blocksRef = useRef(blocks);
46
+ // why: Set as it is written, so a caller awaiting a turn reads the transcript the turn left.
47
+ const setBlocks = useCallback((value) => {
48
+ blocksRef.current = typeof value === 'function' ? value(blocksRef.current) : value;
49
+ setBlocksState(blocksRef.current);
50
+ }, []);
51
+ const streamBlocksRef = useRef(streamBlocks);
52
+ streamBlocksRef.current = streamBlocks;
53
+ const busyRef = useRef(false);
54
+ const abortRef = useRef(null);
55
+ const turnIdRef = useRef(null);
56
+ const pendingRef = useRef(pendingMessages);
57
+ pendingRef.current = pendingMessages;
58
+ const sessionRef = useRef(session);
59
+ sessionRef.current = session;
60
+ const drainLockRef = useRef(false);
61
+ const allowQueueDrainRef = useRef(false);
62
+ const runPromiseRef = useRef(null);
63
+ const deliveryRef = useRef(delivery);
64
+ // why: A run's end reads the composer as it is then, not as it was when the run began.
65
+ const composerRef = useRef({ draftText, pendingFiles, pendingVoice });
66
+ composerRef.current = { draftText, pendingFiles, pendingVoice };
67
+ const setDelivery = useCallback((next) => {
68
+ deliveryRef.current = next;
69
+ setDeliveryState(next);
70
+ }, []);
71
+ const { cancelPendingStreamFrame, scheduleStreamBlocks } = useStreamFrames(setStreamBlocks);
72
+ const clearComposer = useCallback(() => {
73
+ setDraftText('');
74
+ setPendingFiles([]);
75
+ setPendingVoice([]);
76
+ }, []);
77
+ return {
78
+ blocks,
79
+ setBlocks,
80
+ streamBlocks,
81
+ setStreamBlocks,
82
+ streamBlocksRef,
83
+ draftText,
84
+ setDraftText,
85
+ pendingFiles,
86
+ setPendingFiles,
87
+ pendingVoice,
88
+ setPendingVoice,
89
+ pendingMessages,
90
+ setPendingMessages,
91
+ issues,
92
+ setIssues,
93
+ failure,
94
+ setFailure,
95
+ answering,
96
+ setAnswering,
97
+ busy,
98
+ setBusy,
99
+ chatStarted,
100
+ setChatStarted,
101
+ streaming,
102
+ setStreaming,
103
+ session,
104
+ setSession,
105
+ delivery,
106
+ setDelivery,
107
+ deliveryRef,
108
+ composerRef,
109
+ blocksRef,
110
+ busyRef,
111
+ abortRef,
112
+ turnIdRef,
113
+ pendingRef,
114
+ sessionRef,
115
+ drainLockRef,
116
+ allowQueueDrainRef,
117
+ runPromiseRef,
118
+ cancelPendingStreamFrame,
119
+ scheduleStreamBlocks,
120
+ clearComposer,
121
+ };
122
+ }
@@ -0,0 +1,97 @@
1
+ import { type ComposerPendingMessage, type ComposerProfileInterface, type ComposerRunPhase, type InterfaceTurnSession, type TranscriptBlock } from '@theoremjs/agents/interface';
2
+ import type { PageTools } from '../client/live/live-page-tool.ts';
3
+ import type { ConversationView, TheoremTransport, TurnEventSink } from '../client/transport.ts';
4
+ import { type MessageDelivery } from './use-theorem-chat-state.ts';
5
+ export type UseTheoremChatOptions = {
6
+ transport: TheoremTransport;
7
+ /** Composer interface for the host profile — see `useTheoremInterface`. `null` while loading. */
8
+ iface: ComposerProfileInterface | null;
9
+ /**
10
+ * The ref of a conversation to resume, as `onConversationChange` reported it. Read once, when
11
+ * the chat mounts: the chat draws the conversation as the host keeps it. A stateless profile
12
+ * keeps no conversation, so it has no ref.
13
+ */
14
+ conversation?: string;
15
+ /**
16
+ * The conversation under `conversation` as the host showed it with the ref, as
17
+ * `startConversation` returns it. Read once, when the chat mounts: the chat draws it at once,
18
+ * and does not read it from the host again.
19
+ */
20
+ conversationView?: ConversationView;
21
+ /** The text the composer starts with. Read once, when the chat mounts. */
22
+ initialText?: string;
23
+ /**
24
+ * Each new ref the host issues for the conversation: once the message posts, and again as the
25
+ * reply's runs are saved. Keep the latest to resume the chat later.
26
+ */
27
+ onConversationChange?: (ref: string) => void;
28
+ /**
29
+ * The value chosen for each of the profile's `inputs.slots`. Sent with every turn. A slot you
30
+ * name here is fixed to that value; one you leave out is chosen in the composer, starting at
31
+ * its first allowed value.
32
+ */
33
+ slots?: Record<string, string>;
34
+ /** Told each time the visitor chooses a slot in the composer, with every slot's value. */
35
+ onSlotsChange?: (slots: Record<string, string>) => void;
36
+ /** What the page wants the agent to know: any JSON. The latest value goes with every turn. */
37
+ context?: unknown;
38
+ /** The page's tools, by name: each answers a tool the profile declares with `answeredBy: 'page'`. */
39
+ pageTools?: PageTools;
40
+ /**
41
+ * Every event of every turn, as it arrives and before the transcript takes it: the ones the
42
+ * transcript shows, and the ones it does not (a stage, a session, a guardrail decision).
43
+ */
44
+ onTurnEvent?: TurnEventSink;
45
+ };
46
+ /** What `sendText` hands back: the blocks the turn added, its user message, and the reply. */
47
+ export type SentTurn = {
48
+ blocks: TranscriptBlock[];
49
+ };
50
+ /**
51
+ * Headless chat model: transcript, streaming, composer drafts, pending
52
+ * queue / steer / stash, tool gates. Render it with `@theoremjs/react/ui` or
53
+ * your own components.
54
+ */
55
+ export declare function useTheoremChat({ transport, iface, conversation: initialConversation, conversationView, initialText, onConversationChange, slots, onSlotsChange, context, pageTools, onTurnEvent, }: UseTheoremChatOptions): {
56
+ iface: ComposerProfileInterface | null;
57
+ blocks: TranscriptBlock[];
58
+ chatStarted: boolean;
59
+ draftText: string;
60
+ failure: import("../index.ts").ClientFailure | null;
61
+ /** The answer on its way to a gate, shown on it until the answer settles or fails. */
62
+ answering: import("../index.ts").AnsweringGate | null;
63
+ issues: import("@theoremjs/agents").AttachmentValidationIssue[];
64
+ pendingFiles: File[];
65
+ pendingMessages: ComposerPendingMessage[];
66
+ pendingVoice: File[];
67
+ phase: ComposerRunPhase;
68
+ session: InterfaceTurnSession;
69
+ /** The latest message's delivery; `null` before the first and once its turn failed. */
70
+ delivery: MessageDelivery | null;
71
+ streamBlocks: TranscriptBlock[];
72
+ streaming: boolean;
73
+ setDraftText: import("react").Dispatch<import("react").SetStateAction<string>>;
74
+ setPendingFiles: import("react").Dispatch<import("react").SetStateAction<File[]>>;
75
+ setPendingVoice: import("react").Dispatch<import("react").SetStateAction<File[]>>;
76
+ setPendingMessages: import("react").Dispatch<import("react").SetStateAction<ComposerPendingMessage[]>>;
77
+ setIssues: import("react").Dispatch<import("react").SetStateAction<import("@theoremjs/agents").AttachmentValidationIssue[]>>;
78
+ handleAuthenticated: (_index: number, secret?: string) => Promise<void>;
79
+ handleBranch: (index: number) => Promise<void>;
80
+ handleSubmit: () => Promise<void>;
81
+ handleStop: () => void;
82
+ handleMenuAction: (action: import("@theoremjs/agents/interface").ComposerMenuAction) => void;
83
+ handlePendingQueue: (id: string) => void;
84
+ handlePendingRestore: (id: string) => Promise<void>;
85
+ handleSendNow: (draftSource?: ComposerPendingMessage) => Promise<void>;
86
+ handleToolDecision: (_index: number, action: import("../index.ts").ToolDecisionAction) => Promise<void>;
87
+ handleGenerationChange: (next: {
88
+ modelId: string;
89
+ effort?: string;
90
+ }) => void;
91
+ slotChoices: {
92
+ declared: Record<string, string[]>;
93
+ values: Record<string, string>;
94
+ onChange: (name: string, value: string) => void;
95
+ } | undefined;
96
+ sendText: (text: string, files?: readonly File[]) => Promise<SentTurn | null>;
97
+ };