@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,207 @@
1
+ import { attachmentsRefused, TheoremError } from '@theoremjs/agents';
2
+ import { gatedToolsFromEvents, settlesToolCall, } from '@theoremjs/agents/interface';
3
+ import { attachPreviewData, encodeFiles } from "./encode-files.js";
4
+ import { turnFailure } from "./failure.js";
5
+ import { streamFoldedTurn } from "./run-commit.js";
6
+ import { continueGatedToolInvocation } from "./tool-resume.js";
7
+ import { isUnknownConversation, } from "./transport.js";
8
+ import { buildInvokeRequest, buildTurnRequest, foldAssistantTurn, issuedOperationId, prepareComposerTurn, projectUserTurn, } from "./turn-client.js";
9
+ /**
10
+ * Whether the host keeps this chat's conversation. A stateless profile keeps
11
+ * none: the page holds its transcript, and nothing goes on from a ref.
12
+ */
13
+ export function keepsConversation(iface) {
14
+ return iface.providerContinuation?.history !== 'stateless';
15
+ }
16
+ /**
17
+ * A session state the user can't act from (no gate waiting, a gate without a
18
+ * call id): the labels' line for it, as a turn failure.
19
+ */
20
+ function sessionFailure(key, iface) {
21
+ return turnFailure(new TheoremError('request', `session state: ${key}`, { copy: { key } }), iface.labels);
22
+ }
23
+ /** A run that never went out: nothing streamed, and the message did not post. */
24
+ function unsent(session, failure) {
25
+ return { session, events: [], posted: false, failure };
26
+ }
27
+ async function streamPreparedTurn(args) {
28
+ const keeps = keepsConversation(args.iface);
29
+ const userBlocks = attachPreviewData(args.prepared.blocks, args.encodedAttachments, args.encodedVoice);
30
+ const paused = args.session.assistantEvents;
31
+ const walking = args.session.gatedTool !== null;
32
+ const request = buildTurnRequest(args.iface, args.session, {
33
+ ...(args.prepared.draft.text ? { text: args.prepared.draft.text } : {}),
34
+ ...(args.encodedAttachments?.length ? { attachments: args.encodedAttachments } : {}),
35
+ ...(args.encodedVoice?.length ? { voice: args.encodedVoice } : {}),
36
+ }, {
37
+ ...args.scope,
38
+ ...(walking ? { walkAway: { calls: gatedToolsFromEvents(paused) } } : {}),
39
+ });
40
+ // why: Walking away, the stream settles the paused reply first; once the message posts, the events are the new reply's.
41
+ const events = walking ? [...paused] : [];
42
+ let posted = !keeps;
43
+ if (!walking)
44
+ args.onUserBlocks(userBlocks);
45
+ const onConversation = (line) => {
46
+ if (!line.posted)
47
+ return;
48
+ posted = true;
49
+ if (walking) {
50
+ args.onUserBlocks([...foldAssistantTurn(args.iface, events), ...userBlocks]);
51
+ events.length = 0;
52
+ }
53
+ };
54
+ const send = (body) => streamFoldedTurn({
55
+ iface: args.iface,
56
+ view: args.view,
57
+ events,
58
+ onConversation,
59
+ stream: (onLine) => args.transport.turn(body, onLine, args.signal),
60
+ });
61
+ try {
62
+ try {
63
+ await send(request);
64
+ }
65
+ catch (err) {
66
+ if (!args.transcript || request.conversation === undefined || !isUnknownConversation(err))
67
+ throw err;
68
+ // why: The host lost the conversation before the message posted; it goes on from the page's copy, once. The lost reply's waiting calls went with it.
69
+ const { abandon: _lost, ...rest } = request;
70
+ await send({ ...rest, transcript: [...args.transcript] });
71
+ }
72
+ }
73
+ catch (err) {
74
+ return {
75
+ session: args.session,
76
+ // why: A walk that failed before the message posted leaves no reply of its own; the host's view is the truth.
77
+ events: posted ? events : [],
78
+ posted,
79
+ failure: turnFailure(err, args.iface.labels, args.signal),
80
+ };
81
+ }
82
+ if (!posted) {
83
+ // why: A host that keeps the conversation posts the message or fails; a stream that ends without either is the host's fault.
84
+ return unsent(args.session, turnFailure(
85
+ // labels-exempt: internal diagnostic; the user reads error.internal
86
+ new TheoremError('internal', 'the reply ended without posting the message'), args.iface.labels));
87
+ }
88
+ return { session: args.session, events, posted };
89
+ }
90
+ function extractInlineEncodedAttachments(attachments) {
91
+ const filtered = attachments
92
+ ?.filter((a) => typeof a.data === 'string')
93
+ .map((a) => ({ name: a.name, mimeType: a.mimeType, data: a.data }));
94
+ return filtered && filtered.length > 0 ? filtered : undefined;
95
+ }
96
+ async function runPreparedTurnStream(args) {
97
+ if (args.session.gatedTool !== null && (!args.walkAway || !keepsConversation(args.iface))) {
98
+ return unsent(args.session, sessionFailure('session.gate_pending', args.iface));
99
+ }
100
+ try {
101
+ const outcome = await args.prepare();
102
+ if (!outcome.ok) {
103
+ return unsent(args.session, {
104
+ ...turnFailure(attachmentsRefused(outcome.issues), args.iface.labels),
105
+ issues: outcome.issues,
106
+ });
107
+ }
108
+ return await streamPreparedTurn({
109
+ ...args,
110
+ prepared: outcome.prepared,
111
+ ...(outcome.encodedAttachments ? { encodedAttachments: outcome.encodedAttachments } : {}),
112
+ ...(outcome.encodedVoice ? { encodedVoice: outcome.encodedVoice } : {}),
113
+ });
114
+ }
115
+ catch (err) {
116
+ return unsent(args.session, turnFailure(err, args.iface.labels, args.signal));
117
+ }
118
+ }
119
+ export async function streamInterfaceTurn(args) {
120
+ return await runPreparedTurnStream({
121
+ ...args,
122
+ prepare: async () => {
123
+ const prepared = prepareComposerTurn(args.iface, args.text, args.pendingFiles, args.pendingVoice);
124
+ if (!prepared.ok)
125
+ return prepared;
126
+ return {
127
+ ok: true,
128
+ prepared,
129
+ encodedAttachments: args.pendingFiles.length
130
+ ? await encodeFiles(args.pendingFiles)
131
+ : undefined,
132
+ encodedVoice: args.pendingVoice.length ? await encodeFiles(args.pendingVoice) : undefined,
133
+ };
134
+ },
135
+ });
136
+ }
137
+ /** Send a turn from an already-encoded pending draft (queue drain / send now). */
138
+ export async function streamInterfaceDraftTurn(args) {
139
+ return await runPreparedTurnStream({
140
+ ...args,
141
+ prepare: () => {
142
+ const prepared = projectUserTurn(args.iface, args.draft);
143
+ if (!prepared.ok)
144
+ return prepared;
145
+ return {
146
+ ok: true,
147
+ prepared,
148
+ encodedAttachments: extractInlineEncodedAttachments(prepared.draft.attachments),
149
+ encodedVoice: extractInlineEncodedAttachments(prepared.draft.voice),
150
+ };
151
+ },
152
+ });
153
+ }
154
+ /**
155
+ * Send the user's answer to the gate the session waits on. The host settles
156
+ * the call either way (an approval runs it; a refusal records it declined), so
157
+ * the model and the trace read what the user chose. A host that keeps the
158
+ * conversation goes on with the reply in the same stream once no call waits.
159
+ */
160
+ export async function resumeInterfaceTool(args) {
161
+ const gated = args.session.gatedTool;
162
+ if (!gated?.callId)
163
+ return unsent(args.session, sessionFailure('session.gate_expired', args.iface));
164
+ const reply = continueGatedToolInvocation({
165
+ toolName: gated.name,
166
+ gate: gated,
167
+ sessionPermissions: args.session.sessionPermissions,
168
+ resolution: args.resolution,
169
+ });
170
+ const session = {
171
+ ...args.session,
172
+ sessionPermissions: reply.decision === 'approve' ? reply.sessionPermissions : args.session.sessionPermissions,
173
+ };
174
+ // why: Seeded with the paused reply, and filled as the answer streams, so a failed answer keeps what it delivered.
175
+ const events = [...args.session.assistantEvents];
176
+ try {
177
+ await streamFoldedTurn({
178
+ iface: args.iface,
179
+ view: args.view,
180
+ events,
181
+ stream: (onLine) => args.transport.invoke(buildInvokeRequest(args.iface, args.session, {
182
+ gateId: gated.callId,
183
+ operationId: issuedOperationId(gated),
184
+ decision: reply.decision,
185
+ name: gated.name,
186
+ input: gated.arguments,
187
+ sessionPermissions: session.sessionPermissions,
188
+ ...(reply.decision === 'approve' && reply.secret !== undefined
189
+ ? { secret: reply.secret }
190
+ : {}),
191
+ ...(reply.decision === 'approve' && reply.page ? { page: reply.page } : {}),
192
+ }, args.scope), onLine),
193
+ });
194
+ }
195
+ catch (err) {
196
+ // why: An answer that failed before its call settled leaves the reply waiting as it was; the answer can go again.
197
+ const answered = events.slice(args.session.assistantEvents.length);
198
+ const settled = answered.some((event) => settlesToolCall(event, gated.callId));
199
+ return {
200
+ session,
201
+ events: settled ? events : [...args.session.assistantEvents],
202
+ posted: true,
203
+ failure: turnFailure(err, args.iface.labels),
204
+ };
205
+ }
206
+ return { session, events, posted: true };
207
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The rules behind a tool's request form: which input each JSON Schema field
3
+ * gets, what the form starts filled with, and what a request still lacks.
4
+ * The component only maps each decision to its Astryx input.
5
+ *
6
+ * @module
7
+ */
8
+ export type JsonSchema = Record<string, unknown>;
9
+ /** The input a field gets. */
10
+ export type SchemaControl = {
11
+ kind: 'choice';
12
+ options: string[];
13
+ isMenu: boolean;
14
+ } | {
15
+ kind: 'range';
16
+ min: number;
17
+ max: number;
18
+ } | {
19
+ kind: 'number';
20
+ isInteger: boolean;
21
+ min?: number;
22
+ max?: number;
23
+ } | {
24
+ kind: 'switch';
25
+ } | {
26
+ kind: 'date';
27
+ } | {
28
+ kind: 'text';
29
+ isLong: boolean;
30
+ type: 'text' | 'email';
31
+ } | {
32
+ kind: 'choices';
33
+ options: string[];
34
+ } | {
35
+ kind: 'words';
36
+ } | {
37
+ kind: 'rows';
38
+ item: JsonSchema;
39
+ } | {
40
+ kind: 'group';
41
+ schema: JsonSchema;
42
+ } | {
43
+ kind: 'json';
44
+ };
45
+ export type SchemaField = {
46
+ key: string;
47
+ label: string;
48
+ description?: string;
49
+ isRequired: boolean;
50
+ control: SchemaControl;
51
+ };
52
+ /** The input a field's schema asks for. */
53
+ export declare function schemaControl(prop: JsonSchema): SchemaControl;
54
+ /** An object schema's fields in the order it declares them, each with its input. */
55
+ export declare function schemaFields(schema: JsonSchema): SchemaField[];
56
+ /** Required fields, and optional ones the schema gives an example for; a plain value of the field's type when the schema declares no sample. */
57
+ export declare function sampleFromSchema(schema: JsonSchema): Record<string, unknown>;
58
+ /** A new row for a list of objects: its required fields, each at its sample. */
59
+ export declare function blankRow(item: JsonSchema): Record<string, unknown>;
60
+ /** The object with `key` set, or dropped when the value is empty. An optional field left empty isn't sent. */
61
+ export declare function withField(row: Record<string, unknown>, key: string, value: unknown): Record<string, unknown>;
62
+ /** The labels of required fields the request is still missing, nested ones by their path. */
63
+ export declare function missingFields(schema: JsonSchema, value: unknown, prefix?: string): string[];
@@ -0,0 +1,199 @@
1
+ /**
2
+ * The rules behind a tool's request form: which input each JSON Schema field
3
+ * gets, what the form starts filled with, and what a request still lacks.
4
+ * The component only maps each decision to its Astryx input.
5
+ *
6
+ * @module
7
+ */
8
+ import { humanize, isRow } from "./shaped-data.js";
9
+ /** Choices this few sit side by side; more open a menu. */
10
+ const MAX_SEGMENTS = 5;
11
+ /** Whole numbers bounded this closely slide; wider ranges are typed. */
12
+ const MAX_SLIDER_STEPS = 100;
13
+ /** Text allowed this long gets a text area. */
14
+ const LONG_TEXT = 120;
15
+ function kindOf(prop) {
16
+ const declared = Array.isArray(prop.type) ? prop.type.find((type) => type !== 'null') : prop.type;
17
+ if (declared === 'string' ||
18
+ declared === 'number' ||
19
+ declared === 'integer' ||
20
+ declared === 'boolean' ||
21
+ declared === 'array')
22
+ return declared;
23
+ if (declared === 'object' || isRow(prop.properties))
24
+ return 'object';
25
+ if (Array.isArray(prop.enum))
26
+ return 'string';
27
+ return 'unknown';
28
+ }
29
+ function itemsOf(prop) {
30
+ return isRow(prop.items) ? prop.items : undefined;
31
+ }
32
+ function enumOf(prop) {
33
+ const options = prop?.enum;
34
+ return Array.isArray(options) &&
35
+ options.length > 0 &&
36
+ options.every((option) => typeof option === 'string')
37
+ ? options
38
+ : undefined;
39
+ }
40
+ function numberAt(prop, key) {
41
+ const value = prop[key];
42
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
43
+ }
44
+ function numberControl(prop, isInteger) {
45
+ const min = numberAt(prop, 'minimum');
46
+ const max = numberAt(prop, 'maximum');
47
+ if (isInteger &&
48
+ min !== undefined &&
49
+ max !== undefined &&
50
+ max > min &&
51
+ max - min <= MAX_SLIDER_STEPS)
52
+ return { kind: 'range', min, max };
53
+ return {
54
+ kind: 'number',
55
+ isInteger,
56
+ ...(min === undefined ? {} : { min }),
57
+ ...(max === undefined ? {} : { max }),
58
+ };
59
+ }
60
+ function textControl(prop) {
61
+ if (prop.format === 'date')
62
+ return { kind: 'date' };
63
+ const maxLength = numberAt(prop, 'maxLength');
64
+ return {
65
+ kind: 'text',
66
+ isLong: maxLength === undefined ? false : maxLength > LONG_TEXT,
67
+ type: prop.format === 'email' ? 'email' : 'text',
68
+ };
69
+ }
70
+ function listControl(prop) {
71
+ const item = itemsOf(prop);
72
+ const choices = enumOf(item);
73
+ if (choices)
74
+ return { kind: 'choices', options: choices };
75
+ const itemKind = item ? kindOf(item) : 'unknown';
76
+ if (itemKind === 'string' || itemKind === 'number' || itemKind === 'integer')
77
+ return { kind: 'words' };
78
+ if (item && itemKind === 'object')
79
+ return { kind: 'rows', item };
80
+ return { kind: 'json' };
81
+ }
82
+ /** The input a field's schema asks for. */
83
+ export function schemaControl(prop) {
84
+ const options = enumOf(prop);
85
+ if (options)
86
+ return { kind: 'choice', options, isMenu: options.length > MAX_SEGMENTS };
87
+ switch (kindOf(prop)) {
88
+ case 'boolean':
89
+ return { kind: 'switch' };
90
+ case 'number':
91
+ return numberControl(prop, false);
92
+ case 'integer':
93
+ return numberControl(prop, true);
94
+ case 'string':
95
+ return textControl(prop);
96
+ case 'array':
97
+ return listControl(prop);
98
+ case 'object':
99
+ return isRow(prop.properties) ? { kind: 'group', schema: prop } : { kind: 'json' };
100
+ default:
101
+ return { kind: 'json' };
102
+ }
103
+ }
104
+ function requiredOf(schema) {
105
+ return new Set(Array.isArray(schema.required)
106
+ ? schema.required.filter((key) => typeof key === 'string')
107
+ : []);
108
+ }
109
+ /** An object schema's fields in the order it declares them, each with its input. */
110
+ export function schemaFields(schema) {
111
+ const props = isRow(schema.properties) ? schema.properties : {};
112
+ const required = requiredOf(schema);
113
+ return Object.entries(props).flatMap(([key, prop]) => {
114
+ if (!isRow(prop))
115
+ return [];
116
+ return [
117
+ {
118
+ key,
119
+ label: typeof prop.title === 'string' && prop.title ? prop.title : humanize(key),
120
+ ...(typeof prop.description === 'string' && prop.description
121
+ ? { description: prop.description }
122
+ : {}),
123
+ isRequired: required.has(key),
124
+ control: schemaControl(prop),
125
+ },
126
+ ];
127
+ });
128
+ }
129
+ /** The schema's own example for a value: `examples[0]`, then `default`, `const`, `enum[0]`. */
130
+ function declaredSample(prop) {
131
+ if (Array.isArray(prop.examples) && prop.examples.length)
132
+ return { value: prop.examples[0] };
133
+ if ('default' in prop)
134
+ return { value: prop.default };
135
+ if ('const' in prop)
136
+ return { value: prop.const };
137
+ if (Array.isArray(prop.enum) && prop.enum.length)
138
+ return { value: prop.enum[0] };
139
+ return undefined;
140
+ }
141
+ function sampleValue(prop) {
142
+ const declared = declaredSample(prop);
143
+ if (declared)
144
+ return declared.value;
145
+ switch (kindOf(prop)) {
146
+ case 'number':
147
+ case 'integer':
148
+ return numberAt(prop, 'minimum') ?? 1;
149
+ case 'boolean':
150
+ return true;
151
+ case 'array':
152
+ return [];
153
+ case 'object':
154
+ return sampleFromSchema(prop);
155
+ case 'string':
156
+ case 'unknown':
157
+ return 'example';
158
+ }
159
+ }
160
+ /** Required fields, and optional ones the schema gives an example for; a plain value of the field's type when the schema declares no sample. */
161
+ export function sampleFromSchema(schema) {
162
+ const props = isRow(schema.properties) ? schema.properties : {};
163
+ const required = new Set(requiredOf(schema));
164
+ const sample = {};
165
+ for (const [key, prop] of Object.entries(props)) {
166
+ if (!isRow(prop))
167
+ continue;
168
+ if (required.has(key))
169
+ sample[key] = sampleValue(prop);
170
+ else if (Array.isArray(prop.examples) && prop.examples.length)
171
+ sample[key] = prop.examples[0];
172
+ }
173
+ return sample;
174
+ }
175
+ /** A new row for a list of objects: its required fields, each at its sample. */
176
+ export function blankRow(item) {
177
+ return sampleFromSchema(item);
178
+ }
179
+ /** The object with `key` set, or dropped when the value is empty. An optional field left empty isn't sent. */
180
+ export function withField(row, key, value) {
181
+ const next = { ...row };
182
+ if (value === undefined || value === '')
183
+ delete next[key];
184
+ else
185
+ next[key] = value;
186
+ return next;
187
+ }
188
+ /** The labels of required fields the request is still missing, nested ones by their path. */
189
+ export function missingFields(schema, value, prefix = '') {
190
+ if (!isRow(value))
191
+ return [];
192
+ return schemaFields(schema).flatMap((field) => {
193
+ const entry = value[field.key];
194
+ const label = prefix ? `${prefix} › ${field.label}` : field.label;
195
+ if (entry === undefined || entry === null || entry === '')
196
+ return field.isRequired ? [label] : [];
197
+ return field.control.kind === 'group' ? missingFields(field.control.schema, entry, label) : [];
198
+ });
199
+ }
@@ -0,0 +1,118 @@
1
+ /**
2
+ * The rules `ShapedData` draws a payload by, as plain decisions: which shape a
3
+ * value takes, what a row is called, what a field's label and unit are. The
4
+ * component only maps each decision to its Astryx part.
5
+ */
6
+ export type Row = Record<string, unknown>;
7
+ /** Rows shown before the rest are left to the JSON view. */
8
+ export declare const MAX_ROWS = 50;
9
+ export declare const ISO_DATE: RegExp;
10
+ export declare function isRow(value: unknown): value is Row;
11
+ export declare function isPlain(value: unknown): boolean;
12
+ /** The value as indented JSON, whatever it holds. */
13
+ export declare function json(value: unknown): string;
14
+ /** A string that holds a JSON object or list, parsed; anything else as it came. */
15
+ export declare function unpacked(value: unknown): unknown;
16
+ /** `country_code`, `countryCode` → "Country code". */
17
+ export declare function humanize(key: string): string;
18
+ /** A unit worth printing: a symbol or abbreviation, not a format name like `iso8601` or `wmo code`. */
19
+ export declare function printableUnit(unit: unknown): string | undefined;
20
+ /** A column's label and unit: from its `<key>_units` entry, else its key's suffix. */
21
+ export declare function fieldLabel(key: string, units?: Row): {
22
+ label: string;
23
+ unit?: string;
24
+ };
25
+ /** A field's label and the unit its value reads with. A unit only reads onto a number: `country_code` stays "Country code". */
26
+ export declare function fieldReading(key: string, value: unknown, units?: Row): {
27
+ label: string;
28
+ unit?: string;
29
+ };
30
+ /** The `<key>_units` object beside `key`, if there is one. */
31
+ export declare function unitsOf(row: Row, key: string): Row | undefined;
32
+ /** A row's fields, less the units objects its other fields read. */
33
+ export declare function shownEntries(row: Row): [string, unknown][];
34
+ /** How a plain value reads. */
35
+ export type PlainReading = {
36
+ kind: 'none';
37
+ } | {
38
+ kind: 'boolean';
39
+ value: boolean;
40
+ } | {
41
+ kind: 'number';
42
+ value: number;
43
+ unit?: string;
44
+ } | {
45
+ kind: 'link';
46
+ href: string;
47
+ } | {
48
+ kind: 'date';
49
+ date: Date;
50
+ } | {
51
+ kind: 'date-time';
52
+ date: Date;
53
+ } | {
54
+ kind: 'text';
55
+ text: string;
56
+ };
57
+ export declare function plainReading(value: unknown, unit?: string): PlainReading;
58
+ /** A formatted number with its unit: symbols sit close (21°C, 35%), words stand apart (320 m). */
59
+ export declare function withUnit(number: string, unit?: string): string;
60
+ /** The field that names a row, if it has one. */
61
+ export declare function titleKey(row: Row): string | undefined;
62
+ /** A row of a list as a ListItem shows it. */
63
+ export type ListRow = {
64
+ title: string;
65
+ description: string;
66
+ href?: string;
67
+ tags: string[];
68
+ extras: [string, unknown][];
69
+ };
70
+ /** A collapsible row's heading and what it opens to: a titled row loses its title field; else it's named by its place. */
71
+ export declare function rowHeading(item: unknown): {
72
+ title?: string;
73
+ tags: string[];
74
+ rest: unknown;
75
+ };
76
+ /** An object of equal-length lists of plain values (a column store): a table's columns. */
77
+ export declare function columnStore(row: Row): string[] | undefined;
78
+ /** A column store's lists, turned into rows. */
79
+ export declare function storeRows(row: Row, columns: readonly string[]): Row[];
80
+ /** The shape a value is drawn as. */
81
+ export type Shape = {
82
+ kind: 'json';
83
+ } | {
84
+ kind: 'none';
85
+ } | {
86
+ kind: 'plain';
87
+ } | {
88
+ kind: 'tokens';
89
+ items: unknown[];
90
+ } | {
91
+ kind: 'list';
92
+ rows: ListRow[];
93
+ } | {
94
+ kind: 'table';
95
+ columns: string[];
96
+ rows: Row[];
97
+ } | {
98
+ kind: 'rows';
99
+ items: unknown[];
100
+ } | {
101
+ kind: 'fields';
102
+ row: Row;
103
+ };
104
+ /** How a value (already unpacked) at `depth` is drawn. */
105
+ export declare function shapeOf(value: unknown, depth: number): Shape;
106
+ export declare function isProse(value: unknown): value is string;
107
+ /** A nested field, through any chain of one-key wrappers: `{ data: { items: [] } }` → "Data › Items". */
108
+ export type Section = {
109
+ key: string;
110
+ title: string;
111
+ value: unknown;
112
+ units?: Row;
113
+ };
114
+ /** An object's fields split: plain ones for the field list, nested ones as sections. */
115
+ export declare function splitFields(row: Row): {
116
+ plain: [string, unknown][];
117
+ sections: Section[];
118
+ };