@theoremjs/react 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/README.md +728 -202
  2. package/client/composer-hints.js +1 -1
  3. package/client/decision-images.d.ts +76 -0
  4. package/client/decision-images.js +139 -0
  5. package/client/decision-transport.d.ts +26 -4
  6. package/client/decision-transport.js +79 -6
  7. package/client/embedding-input.d.ts +84 -0
  8. package/client/embedding-input.js +194 -0
  9. package/client/embedding-space.d.ts +82 -0
  10. package/client/embedding-space.js +389 -0
  11. package/client/embedding-transport.d.ts +40 -0
  12. package/client/embedding-transport.js +71 -0
  13. package/client/failure.d.ts +7 -10
  14. package/client/failure.js +9 -9
  15. package/client/host-call.d.ts +1 -1
  16. package/client/host-call.js +7 -2
  17. package/client/host-transport.d.ts +2 -0
  18. package/client/host-transport.js +3 -2
  19. package/client/index.d.ts +19 -11
  20. package/client/index.js +14 -10
  21. package/client/ink-waveform.d.ts +1 -1
  22. package/client/ink-waveform.js +1 -2
  23. package/client/live/apply-live-turn-tool-event.js +1 -1
  24. package/client/live/live-captions.d.ts +30 -0
  25. package/client/live/live-captions.js +105 -1
  26. package/client/live/live-mic-forward.d.ts +1 -0
  27. package/client/live/live-mic-forward.js +11 -2
  28. package/client/live/live-page-tool.d.ts +34 -0
  29. package/client/live/live-page-tool.js +11 -0
  30. package/client/live/live-state.d.ts +4 -2
  31. package/client/live/live-state.js +2 -0
  32. package/client/live/live-video.js +1 -1
  33. package/client/live/run-live-tool-call.d.ts +7 -2
  34. package/client/live/run-live-tool-call.js +48 -10
  35. package/client/live/use-live-caption-log.d.ts +17 -0
  36. package/client/live/use-live-caption-log.js +51 -0
  37. package/client/live-client.d.ts +52 -4
  38. package/client/live-client.js +250 -59
  39. package/client/live-messages.d.ts +51 -13
  40. package/client/live-messages.js +42 -9
  41. package/client/page-dispatch.d.ts +15 -0
  42. package/client/page-dispatch.js +63 -0
  43. package/client/run-commit.d.ts +16 -43
  44. package/client/run-commit.js +26 -101
  45. package/client/run-session.d.ts +40 -41
  46. package/client/run-session.js +104 -112
  47. package/client/schema-fields.js +27 -6
  48. package/client/shaped-data.js +37 -8
  49. package/client/source-citations.js +1 -1
  50. package/client/space-geometry.d.ts +39 -0
  51. package/client/space-geometry.js +183 -0
  52. package/client/state-fields.js +1 -1
  53. package/client/tool-result.js +30 -12
  54. package/client/tool-resume.d.ts +18 -1
  55. package/client/tool-resume.js +4 -1
  56. package/client/trace-feed.d.ts +1 -1
  57. package/client/trace-story.d.ts +2 -2
  58. package/client/trace-story.js +43 -19
  59. package/client/trace-view.d.ts +2 -2
  60. package/client/trace-view.js +56 -16
  61. package/client/transcript-groups.d.ts +28 -2
  62. package/client/transcript-groups.js +63 -13
  63. package/client/transcript-schema.d.ts +10 -0
  64. package/client/transcript-schema.js +120 -0
  65. package/client/transport.d.ts +114 -26
  66. package/client/transport.js +106 -32
  67. package/client/turn-client.d.ts +17 -8
  68. package/client/turn-client.js +26 -31
  69. package/client/voice-recorder.d.ts +1 -1
  70. package/client/voice-recorder.js +2 -5
  71. package/client/wire-line.js +5 -5
  72. package/client/worklets/mic-capture.js +9 -9
  73. package/components/InkWaveform.d.ts +12 -1
  74. package/components/InkWaveform.js +65 -42
  75. package/components/live/use-live-runner-controls.d.ts +3 -4
  76. package/components/live/use-live-runner-controls.js +6 -10
  77. package/components/live/use-live-runner-model.d.ts +10 -6
  78. package/components/live/use-live-runner-model.js +29 -14
  79. package/components/live/use-live-runner-ui.d.ts +10 -8
  80. package/components/live/use-live-runner-ui.js +19 -11
  81. package/components/live/use-live-session-client.d.ts +4 -4
  82. package/components/live/use-live-session-client.js +11 -8
  83. package/components/use-composer-voice.d.ts +4 -4
  84. package/components/use-composer-voice.js +8 -8
  85. package/components/use-editor-selection.js +4 -1
  86. package/components/use-voice-playback.d.ts +2 -1
  87. package/components/use-voice-playback.js +4 -4
  88. package/hooks/use-described.d.ts +5 -1
  89. package/hooks/use-described.js +11 -9
  90. package/hooks/use-embedding-index.d.ts +27 -0
  91. package/hooks/use-embedding-index.js +43 -0
  92. package/hooks/use-one-call.d.ts +31 -0
  93. package/hooks/use-one-call.js +76 -0
  94. package/hooks/use-page-tool-warnings.d.ts +6 -0
  95. package/hooks/use-page-tool-warnings.js +25 -0
  96. package/hooks/use-second-ticker.d.ts +2 -0
  97. package/hooks/use-second-ticker.js +13 -0
  98. package/hooks/use-theorem-chat-actions.d.ts +25 -21
  99. package/hooks/use-theorem-chat-actions.js +67 -45
  100. package/hooks/use-theorem-chat-state.d.ts +6 -6
  101. package/hooks/use-theorem-chat-state.js +13 -9
  102. package/hooks/use-theorem-chat.d.ts +48 -14
  103. package/hooks/use-theorem-chat.js +329 -141
  104. package/hooks/use-theorem-decision.d.ts +5 -10
  105. package/hooks/use-theorem-decision.js +4 -67
  106. package/hooks/use-theorem-embedding.d.ts +18 -0
  107. package/hooks/use-theorem-embedding.js +12 -0
  108. package/hooks/use-theorem-host.d.ts +5 -4
  109. package/hooks/use-theorem-host.js +26 -8
  110. package/hooks/use-theorem-interface.d.ts +6 -3
  111. package/hooks/use-theorem-interface.js +12 -22
  112. package/index.d.ts +6 -3
  113. package/index.js +5 -2
  114. package/live.d.ts +1 -0
  115. package/live.js +1 -1
  116. package/package.json +6 -5
  117. package/server/checkpoint-ownership.d.ts +6 -0
  118. package/server/checkpoint-ownership.js +42 -0
  119. package/server/conversation-store.d.ts +92 -0
  120. package/server/conversation-store.js +131 -0
  121. package/server/conversation.d.ts +193 -0
  122. package/server/conversation.js +552 -0
  123. package/server/decision-handler.d.ts +12 -4
  124. package/server/decision-handler.js +29 -38
  125. package/server/embedding-handler.d.ts +33 -0
  126. package/server/embedding-handler.js +45 -0
  127. package/server/handler.d.ts +58 -20
  128. package/server/handler.js +441 -238
  129. package/server/mod.d.ts +15 -6
  130. package/server/mod.js +13 -5
  131. package/server/page-dispatch.d.ts +22 -0
  132. package/server/page-dispatch.js +127 -0
  133. package/server/request-body.d.ts +2 -0
  134. package/server/request-body.js +42 -0
  135. package/server/request-check.d.ts +6 -1
  136. package/server/request-check.js +27 -12
  137. package/server/session-authority.d.ts +8 -0
  138. package/server/session-authority.js +78 -0
  139. package/server/session-boundary.d.ts +26 -0
  140. package/server/session-boundary.js +64 -0
  141. package/server/session-identity.d.ts +16 -0
  142. package/server/session-identity.js +41 -0
  143. package/server/session-limits.d.ts +12 -0
  144. package/server/session-limits.js +59 -0
  145. package/server/session-store.d.ts +69 -20
  146. package/server/session-store.js +220 -5
  147. package/server/single-action.d.ts +21 -0
  148. package/server/single-action.js +30 -0
  149. package/server/steer-inbox.js +1 -1
  150. package/server/tool-operations.d.ts +6 -0
  151. package/server/tool-operations.js +132 -0
  152. package/server/turn-input.d.ts +10 -0
  153. package/server/turn-input.js +29 -0
  154. package/styles/ink-controls.css +78 -78
  155. package/ui/CardWall.d.ts +10 -0
  156. package/ui/CardWall.js +61 -0
  157. package/ui/ChatComposerBar.d.ts +28 -5
  158. package/ui/ChatComposerBar.js +85 -30
  159. package/ui/ChatTranscript.d.ts +14 -1
  160. package/ui/ChatTranscript.js +110 -57
  161. package/ui/ConsoleFrame.d.ts +61 -4
  162. package/ui/ConsoleFrame.js +109 -9
  163. package/ui/DecisionAnswers.d.ts +40 -2
  164. package/ui/DecisionAnswers.js +166 -35
  165. package/ui/DecisionImages.d.ts +16 -0
  166. package/ui/DecisionImages.js +46 -0
  167. package/ui/EmbeddingDocuments.d.ts +78 -0
  168. package/ui/EmbeddingDocuments.js +278 -0
  169. package/ui/EmbeddingQuery.d.ts +27 -0
  170. package/ui/EmbeddingQuery.js +95 -0
  171. package/ui/EmbeddingResults.d.ts +41 -0
  172. package/ui/EmbeddingResults.js +165 -0
  173. package/ui/EmbeddingSpace.d.ts +26 -0
  174. package/ui/EmbeddingSpace.js +106 -0
  175. package/ui/LiveCaptionsPanel.d.ts +22 -0
  176. package/ui/LiveCaptionsPanel.js +27 -0
  177. package/ui/LiveRunner.d.ts +6 -4
  178. package/ui/LiveRunner.js +42 -55
  179. package/ui/PaneState.d.ts +33 -0
  180. package/ui/PaneState.js +50 -0
  181. package/ui/SchemaFields.d.ts +1 -1
  182. package/ui/SchemaFields.js +54 -26
  183. package/ui/ShapedData.js +73 -28
  184. package/ui/SidePanel.d.ts +74 -5
  185. package/ui/SidePanel.js +82 -9
  186. package/ui/StateFields.d.ts +10 -2
  187. package/ui/StateFields.js +56 -13
  188. package/ui/TheoremChat.d.ts +65 -14
  189. package/ui/TheoremChat.js +66 -78
  190. package/ui/TheoremDecision.d.ts +12 -20
  191. package/ui/TheoremDecision.js +78 -35
  192. package/ui/TheoremEmbedding.d.ts +27 -0
  193. package/ui/TheoremEmbedding.js +122 -0
  194. package/ui/TheoremHost.d.ts +22 -5
  195. package/ui/TheoremHost.js +127 -37
  196. package/ui/ToolGateCard.d.ts +5 -1
  197. package/ui/ToolGateCard.js +30 -23
  198. package/ui/ToolResult.js +12 -10
  199. package/ui/ToolResultCharts.js +29 -12
  200. package/ui/TraceCharts.d.ts +2 -2
  201. package/ui/TraceCharts.js +48 -17
  202. package/ui/TraceGuardrails.d.ts +3 -2
  203. package/ui/TraceGuardrails.js +24 -6
  204. package/ui/TraceInspector.d.ts +11 -1
  205. package/ui/TraceInspector.js +45 -28
  206. package/ui/TraceInspectorPanel.d.ts +31 -9
  207. package/ui/TraceInspectorPanel.js +77 -39
  208. package/ui/TraceOverview.d.ts +7 -3
  209. package/ui/TraceOverview.js +44 -14
  210. package/ui/TraceSpanDetail.js +24 -22
  211. package/ui/TraceStory.js +9 -8
  212. package/ui/TraceValues.js +19 -9
  213. package/ui/TraceWaterfall.js +12 -9
  214. package/ui/VoiceNote.js +4 -4
  215. package/ui/approval-heading.d.ts +4 -0
  216. package/ui/approval-heading.js +9 -0
  217. package/ui/arrive.d.ts +18 -0
  218. package/ui/arrive.js +46 -0
  219. package/ui/built/theme.css +86 -68
  220. package/ui/built/theme.css.d.ts +9 -0
  221. package/ui/built/theorem.d.ts +3 -3
  222. package/ui/built/theorem.js +34 -5
  223. package/ui/built/theorem.variants.d.ts +8 -2
  224. package/ui/code-language.d.ts +12 -0
  225. package/ui/code-language.js +50 -0
  226. package/ui/disclosure-motion.js +53 -9
  227. package/ui/embedding-space-gl.d.ts +37 -0
  228. package/ui/embedding-space-gl.js +663 -0
  229. package/ui/embedding-space-view.d.ts +8 -0
  230. package/ui/embedding-space-view.js +8 -0
  231. package/ui/glide.d.ts +7 -0
  232. package/ui/glide.js +44 -0
  233. package/ui/icons.js +2 -1
  234. package/ui/index.d.ts +20 -9
  235. package/ui/index.js +20 -10
  236. package/ui/labels-provider.js +5 -2
  237. package/ui/labels.d.ts +374 -85
  238. package/ui/labels.js +1028 -188
  239. package/ui/markdown-plugins.d.ts +9 -0
  240. package/ui/markdown-plugins.js +13 -0
  241. package/ui/mcp-icon.d.ts +6 -0
  242. package/ui/mcp-icon.js +8 -0
  243. package/ui/row-keys.d.ts +19 -0
  244. package/ui/row-keys.js +33 -0
  245. package/ui/theme.d.ts +1 -1
  246. package/ui/theme.js +2 -2
  247. package/ui/theorem-theme.js +32 -10
  248. package/ui/trace-palette.d.ts +2 -16
  249. package/ui/trace-palette.js +2 -16
  250. package/ui/trace-patterns.d.ts +11 -3
  251. package/ui/trace-patterns.js +16 -3
  252. package/ui/transcript-copy-text.js +1 -0
  253. package/ui/wall-columns.d.ts +8 -0
  254. package/ui/wall-columns.js +19 -0
  255. package/client/walk-away.d.ts +0 -34
  256. package/client/walk-away.js +0 -48
  257. package/server/walk-away.d.ts +0 -22
  258. package/server/walk-away.js +0 -41
@@ -1,8 +1,9 @@
1
- import { useCallback } from 'react';
2
1
  import { TheoremError } from '@theoremjs/agents';
3
2
  import { convertSteersToFrontQueued, createComposerPendingMessage, orderComposerPendingMessages, removeComposerPendingMessage, userDraftHasPayload, userDraftToSteerInject, } from '@theoremjs/agents/interface';
4
- import { composerFieldsFromDraft, encodeComposerDraft, resumeInterfaceTool, streamInterfaceDraftTurn, streamInterfaceTurn, } from "../client/index.js";
3
+ import { useCallback } from 'react';
5
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";
6
7
  function composerFieldsPayload(text, pendingFiles, pendingVoice) {
7
8
  const meta = (files) => files.map((f) => ({
8
9
  name: f.name,
@@ -33,8 +34,30 @@ function beginAbortableTurn(args) {
33
34
  signal: controller.signal,
34
35
  };
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
+ }
36
59
  /** Starts a turn from the composer's fields, or from an encoded draft (queued or send-now). */
37
- function useTurnStarters(args) {
60
+ function useTurnStarters(args, restoreToComposer) {
38
61
  const startTurnFromFields = useCallback(async (fields) => {
39
62
  const started = beginAbortableTurn(args);
40
63
  if (!started)
@@ -47,8 +70,9 @@ function useTurnStarters(args) {
47
70
  pendingFiles: fields.files,
48
71
  pendingVoice: fields.voice,
49
72
  signal: started.signal,
50
- turnId: started.turnId,
73
+ scope: scopeOf(args, started.turnId),
51
74
  view,
75
+ transcript: args.blocksRef.current,
52
76
  onUserBlocks: (userBlocks) => {
53
77
  args.setBlocks((prev) => [...prev, ...userBlocks]);
54
78
  args.setDelivery({ status: 'sending' });
@@ -56,43 +80,43 @@ function useTurnStarters(args) {
56
80
  args.setStreaming(true);
57
81
  args.clearComposer();
58
82
  },
59
- }), { userBlocksAlreadyApplied: true });
60
- }, [args]);
83
+ }), { message: { restore: () => restoreToComposer(fields) } });
84
+ }, [args, restoreToComposer]);
61
85
  const startTurnFromDraft = useCallback(async (draft, options = {}) => {
62
86
  const started = beginAbortableTurn(args);
63
87
  if (!started)
64
88
  return;
65
- await args.runTurnStream((view, paused) => streamInterfaceDraftTurn({
89
+ await args.runTurnStream((view) => streamInterfaceDraftTurn({
66
90
  iface: started.composer,
67
91
  transport: args.transport,
68
92
  session: args.sessionRef.current,
69
93
  draft,
70
94
  signal: started.signal,
71
- turnId: started.turnId,
95
+ scope: scopeOf(args, started.turnId),
72
96
  view,
73
- ...(options.walkAway ? { walkAway: paused } : {}),
97
+ transcript: args.blocksRef.current,
98
+ walkAway: options.walkAway === true,
74
99
  onUserBlocks: (posted) => {
75
100
  args.setBlocks((prev) => [...prev, ...posted]);
76
- // A walked-away reply's blocks were streaming; they are posted now.
77
101
  args.setStreamBlocks([]);
78
102
  args.setDelivery({ status: 'sending' });
79
103
  args.setChatStarted(true);
80
104
  args.setStreaming(true);
81
105
  options.onPosted?.();
82
106
  },
83
- }), { userBlocksAlreadyApplied: true, walksAway: options.walkAway === true });
84
- }, [args]);
107
+ }), { message: { restore: () => restoreToComposer(composerFieldsFromDraft(draft)) } });
108
+ }, [args, restoreToComposer]);
85
109
  return { startTurnFromFields, startTurnFromDraft };
86
110
  }
87
111
  /** A steer with no running turn: the user reads `session.turn_ended`; queued steers move to the front. */
88
112
  function steerAfterTurnEnded(args, messageId) {
89
113
  args.setFailure(clientFailure(
90
- // lexicon-exempt: internal diagnostic; the user reads session.turn_ended
91
- new TheoremError('request', 'steer: no active turn', { copy: { key: 'session.turn_ended' } }), args.iface?.lexicon));
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));
92
116
  args.setPendingMessages((prev) => orderComposerPendingMessages(convertSteersToFrontQueued(removeComposerPendingMessage(prev, messageId))));
93
117
  }
94
118
  /** Queue / steer / stash the composer draft, and restore a pending message into the composer. */
95
- function usePendingActions(args) {
119
+ function usePendingActions(args, restoreToComposer) {
96
120
  const enqueuePending = useCallback(async (kind) => {
97
121
  if (!userDraftHasPayload(composerFieldsPayload(args.draftText, args.pendingFiles, args.pendingVoice))) {
98
122
  return;
@@ -120,36 +144,21 @@ function usePendingActions(args) {
120
144
  await args.transport.steer({ turnId, id: message.id, inject });
121
145
  }
122
146
  catch (err) {
123
- args.setFailure(clientFailure(err, args.iface?.lexicon));
147
+ args.setFailure(clientFailure(err, args.iface?.labels));
124
148
  }
125
149
  }, [args]);
126
150
  const handlePendingRestore = useCallback(async (id) => {
127
151
  const message = args.pendingRef.current.find((m) => m.id === id);
128
152
  if (!message)
129
153
  return;
130
- const currentDraft = composerFieldsPayload(args.draftText, args.pendingFiles, args.pendingVoice);
131
154
  try {
132
- let nextPending = removeComposerPendingMessage(args.pendingRef.current, id);
133
- if (userDraftHasPayload(currentDraft)) {
134
- const stashDraft = await encodeComposerDraft({
135
- text: args.draftText,
136
- pendingFiles: args.pendingFiles,
137
- pendingVoice: args.pendingVoice,
138
- });
139
- const stash = createComposerPendingMessage({ kind: 'stash', draft: stashDraft });
140
- nextPending = orderComposerPendingMessages([...nextPending, stash]);
141
- }
142
- const restored = composerFieldsFromDraft(message.draft);
143
- args.setPendingMessages(nextPending);
144
- args.setDraftText(restored.text);
145
- args.setPendingFiles(restored.files);
146
- args.setPendingVoice(restored.voice);
147
- args.setIssues([]);
155
+ await restoreToComposer(composerFieldsFromDraft(message.draft));
156
+ args.setPendingMessages((prev) => removeComposerPendingMessage(prev, id));
148
157
  }
149
158
  catch (err) {
150
- args.setFailure(clientFailure(err, args.iface?.lexicon));
159
+ args.setFailure(clientFailure(err, args.iface?.labels));
151
160
  }
152
- }, [args]);
161
+ }, [args, restoreToComposer]);
153
162
  return { enqueuePending, handlePendingRestore };
154
163
  }
155
164
  /** Answer the gate the session waits on: a decision, or a sign-in. */
@@ -158,15 +167,27 @@ function useGateActions(args) {
158
167
  const composer = args.iface;
159
168
  if (!composer)
160
169
  return;
161
- // Set once the run starts (not while another runs), and cleared once it has ended.
170
+ // why: Set once the run starts (not while another runs), and cleared once it has ended.
162
171
  let started = false;
163
172
  try {
164
173
  await args.runTurnStream((view) => {
165
174
  started = true;
166
175
  const session = args.sessionRef.current;
167
- if (session.gatedTool)
168
- args.setAnswering({ callId: session.gatedTool.callId, action: resolution.action });
169
- return resumeInterfaceTool({ iface: composer, transport: args.transport, session, resolution, view });
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
+ });
170
191
  });
171
192
  }
172
193
  finally {
@@ -180,12 +201,13 @@ function useGateActions(args) {
180
201
  const handleAuthenticated = useCallback(async (_index, secret) => {
181
202
  await resumeGatedTool({ action: 'auth', ...(secret === undefined ? {} : { secret }) });
182
203
  }, [resumeGatedTool]);
183
- return { handleToolDecision, handleAuthenticated };
204
+ return { handleToolDecision, handleAuthenticated, resumeGatedTool };
184
205
  }
185
206
  export function useTheoremChatActions(args) {
186
- const { startTurnFromFields, startTurnFromDraft } = useTurnStarters(args);
187
- const { enqueuePending, handlePendingRestore } = usePendingActions(args);
188
- const { handleToolDecision, handleAuthenticated } = useGateActions(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);
189
211
  const handleStop = useCallback(() => {
190
212
  args.abortRef.current?.abort();
191
213
  }, [args]);
@@ -235,10 +257,9 @@ export function useTheoremChatActions(args) {
235
257
  }
236
258
  args.setPendingMessages((prev) => convertSteersToFrontQueued(prev));
237
259
  args.allowQueueDrainRef.current = false;
238
- // Sent while the reply waits on gates, the message walks away from them in its own request.
260
+ // why: Sent while the reply waits on gates, the message walks away from them in its own request.
239
261
  await startTurnFromDraft(draft, {
240
262
  walkAway: args.sessionRef.current.gatedTool !== null,
241
- // The message leaves the composer (or the queue) once it posts.
242
263
  onPosted: () => releaseDraft(draftSource),
243
264
  });
244
265
  }, [args, draftToSend, releaseDraft, startTurnFromDraft]);
@@ -259,6 +280,7 @@ export function useTheoremChatActions(args) {
259
280
  handleMenuAction,
260
281
  handleToolDecision,
261
282
  handleAuthenticated,
283
+ resumeGatedTool,
262
284
  handlePendingRestore,
263
285
  enqueuePending,
264
286
  };
@@ -8,13 +8,8 @@ export type { SetSession };
8
8
  * (`sent`), reached the model (`delivered`), answered (`read`).
9
9
  */
10
10
  export type MessageDelivery = 'sending' | 'sent' | 'delivered' | 'read';
11
- /** A conversation at rest: what the transcript shows and what the next turn is sent with. */
12
- export type ChatSnapshot = {
13
- blocks: TranscriptBlock[];
14
- session: InterfaceTurnSession;
15
- };
16
11
  /** Composer / transcript / session state behind {@link useTheoremChat}. */
17
- export declare function useTheoremChatState(initial?: ChatSnapshot): {
12
+ export declare function useTheoremChatState(initialText?: string): {
18
13
  blocks: TranscriptBlock[];
19
14
  setBlocks: (value: TranscriptBlock[] | ((prev: TranscriptBlock[]) => TranscriptBlock[])) => void;
20
15
  streamBlocks: TranscriptBlock[];
@@ -51,6 +46,11 @@ export declare function useTheoremChatState(initial?: ChatSnapshot): {
51
46
  deliveryRef: import("react").RefObject<{
52
47
  status: MessageDelivery;
53
48
  } | null>;
49
+ composerRef: import("react").RefObject<{
50
+ draftText: string;
51
+ pendingFiles: File[];
52
+ pendingVoice: File[];
53
+ }>;
54
54
  blocksRef: import("react").RefObject<TranscriptBlock[]>;
55
55
  busyRef: import("react").RefObject<boolean>;
56
56
  abortRef: import("react").RefObject<AbortController | null>;
@@ -1,5 +1,5 @@
1
- import { useCallback, useRef, useState } from 'react';
2
1
  import { emptyInterfaceTurnSession, } from '@theoremjs/agents/interface';
2
+ import { useCallback, useRef, useState } from 'react';
3
3
  /** Coalesces streamed partial blocks to one state update per animation frame. */
4
4
  function useStreamFrames(setStreamBlocks) {
5
5
  const streamRafRef = useRef(null);
@@ -22,14 +22,14 @@ function useStreamFrames(setStreamBlocks) {
22
22
  if (next)
23
23
  setStreamBlocks(next);
24
24
  });
25
- }, []);
25
+ }, [setStreamBlocks]);
26
26
  return { cancelPendingStreamFrame, scheduleStreamBlocks };
27
27
  }
28
28
  /** Composer / transcript / session state behind {@link useTheoremChat}. */
29
- export function useTheoremChatState(initial) {
30
- const [blocks, setBlocksState] = useState(initial?.blocks ?? []);
29
+ export function useTheoremChatState(initialText = '') {
30
+ const [blocks, setBlocksState] = useState([]);
31
31
  const [streamBlocks, setStreamBlocks] = useState([]);
32
- const [draftText, setDraftText] = useState('');
32
+ const [draftText, setDraftText] = useState(initialText);
33
33
  const [pendingFiles, setPendingFiles] = useState([]);
34
34
  const [pendingVoice, setPendingVoice] = useState([]);
35
35
  const [pendingMessages, setPendingMessages] = useState([]);
@@ -37,13 +37,13 @@ export function useTheoremChatState(initial) {
37
37
  const [failure, setFailure] = useState(null);
38
38
  const [answering, setAnswering] = useState(null);
39
39
  const [busy, setBusy] = useState(false);
40
- const [chatStarted, setChatStarted] = useState((initial?.blocks.length ?? 0) > 0);
40
+ const [chatStarted, setChatStarted] = useState(false);
41
41
  const [streaming, setStreaming] = useState(false);
42
- const [session, setSession] = useState(initial?.session ?? emptyInterfaceTurnSession());
43
- // One object per posted message, so a run can tell whether it posted one.
42
+ const [session, setSession] = useState(emptyInterfaceTurnSession);
43
+ // why: One object per posted message, so a run can tell whether it posted one.
44
44
  const [delivery, setDeliveryState] = useState(null);
45
45
  const blocksRef = useRef(blocks);
46
- // Set as it is written, so a caller awaiting a turn reads the transcript the turn left.
46
+ // why: Set as it is written, so a caller awaiting a turn reads the transcript the turn left.
47
47
  const setBlocks = useCallback((value) => {
48
48
  blocksRef.current = typeof value === 'function' ? value(blocksRef.current) : value;
49
49
  setBlocksState(blocksRef.current);
@@ -61,6 +61,9 @@ export function useTheoremChatState(initial) {
61
61
  const allowQueueDrainRef = useRef(false);
62
62
  const runPromiseRef = useRef(null);
63
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 };
64
67
  const setDelivery = useCallback((next) => {
65
68
  deliveryRef.current = next;
66
69
  setDeliveryState(next);
@@ -102,6 +105,7 @@ export function useTheoremChatState(initial) {
102
105
  delivery,
103
106
  setDelivery,
104
107
  deliveryRef,
108
+ composerRef,
105
109
  blocksRef,
106
110
  busyRef,
107
111
  abortRef,
@@ -1,20 +1,49 @@
1
1
  import { type ComposerPendingMessage, type ComposerProfileInterface, type ComposerRunPhase, type InterfaceTurnSession, type TranscriptBlock } from '@theoremjs/agents/interface';
2
- import type { TheoremTransport } from '../client/transport.ts';
3
- import { type ChatSnapshot, type MessageDelivery } from './use-theorem-chat-state.ts';
4
- export type { ChatSnapshot };
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
5
  export type UseTheoremChatOptions = {
6
6
  transport: TheoremTransport;
7
7
  /** Composer interface for the host profile — see `useTheoremInterface`. `null` while loading. */
8
8
  iface: ComposerProfileInterface | null;
9
- /** A conversation to resume: its transcript, and the session the next turn continues from. */
10
- initial?: ChatSnapshot;
11
9
  /**
12
- * The conversation each time it comes to rest: a turn finished, or a message was added or
13
- * removed. Never while a reply streams or waits on a gate.
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.
14
13
  */
15
- onChange?: (snapshot: ChatSnapshot) => void;
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;
16
45
  };
17
- /** What `sendText` hands back: the blocks the turn added, its user message and the reply. */
46
+ /** What `sendText` hands back: the blocks the turn added, its user message, and the reply. */
18
47
  export type SentTurn = {
19
48
  blocks: TranscriptBlock[];
20
49
  };
@@ -23,7 +52,7 @@ export type SentTurn = {
23
52
  * queue / steer / stash, tool gates. Render it with `@theoremjs/react/ui` or
24
53
  * your own components.
25
54
  */
26
- export declare function useTheoremChat({ transport, iface, initial, onChange }: UseTheoremChatOptions): {
55
+ export declare function useTheoremChat({ transport, iface, conversation: initialConversation, conversationView, initialText, onConversationChange, slots, onSlotsChange, context, pageTools, onTurnEvent, }: UseTheoremChatOptions): {
27
56
  iface: ComposerProfileInterface | null;
28
57
  blocks: TranscriptBlock[];
29
58
  chatStarted: boolean;
@@ -31,7 +60,7 @@ export declare function useTheoremChat({ transport, iface, initial, onChange }:
31
60
  failure: import("../index.ts").ClientFailure | null;
32
61
  /** The answer on its way to a gate, shown on it until the answer settles or fails. */
33
62
  answering: import("../index.ts").AnsweringGate | null;
34
- issues: import("@theoremjs/agents/interface").AttachmentValidationIssue[];
63
+ issues: import("@theoremjs/agents").AttachmentValidationIssue[];
35
64
  pendingFiles: File[];
36
65
  pendingMessages: ComposerPendingMessage[];
37
66
  pendingVoice: File[];
@@ -45,9 +74,9 @@ export declare function useTheoremChat({ transport, iface, initial, onChange }:
45
74
  setPendingFiles: import("react").Dispatch<import("react").SetStateAction<File[]>>;
46
75
  setPendingVoice: import("react").Dispatch<import("react").SetStateAction<File[]>>;
47
76
  setPendingMessages: import("react").Dispatch<import("react").SetStateAction<ComposerPendingMessage[]>>;
48
- setIssues: import("react").Dispatch<import("react").SetStateAction<import("@theoremjs/agents/interface").AttachmentValidationIssue[]>>;
77
+ setIssues: import("react").Dispatch<import("react").SetStateAction<import("@theoremjs/agents").AttachmentValidationIssue[]>>;
49
78
  handleAuthenticated: (_index: number, secret?: string) => Promise<void>;
50
- handleBranch: (index: number) => void;
79
+ handleBranch: (index: number) => Promise<void>;
51
80
  handleSubmit: () => Promise<void>;
52
81
  handleStop: () => void;
53
82
  handleMenuAction: (action: import("@theoremjs/agents/interface").ComposerMenuAction) => void;
@@ -59,5 +88,10 @@ export declare function useTheoremChat({ transport, iface, initial, onChange }:
59
88
  modelId: string;
60
89
  effort?: string;
61
90
  }) => void;
62
- sendText: (text: string) => Promise<SentTurn | null>;
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>;
63
97
  };