@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,175 @@
1
+ /**
2
+ * The browser side of a decision profile: what the host lets the page see of
3
+ * it, and the one call that asks its questions about some state.
4
+ *
5
+ * @module
6
+ */
7
+ import { resolveObservabilityPolicy, TheoremError, traceRecordSchema, z, } from '@theoremjs/agents';
8
+ import { fetchJson } from "./transport.js";
9
+ import { checkWire } from "./wire-line.js";
10
+ /** A score level's name: its text, or the first text inside a structured entry. */
11
+ function entryLabel(entry, fallback) {
12
+ if (typeof entry === 'string')
13
+ return entry;
14
+ const entries = Array.isArray(entry) ? entry : Object.keys(entry);
15
+ return entries.length ? entryLabel(entries[0], fallback) : fallback;
16
+ }
17
+ function withField(field) {
18
+ return field === undefined ? {} : { field };
19
+ }
20
+ /** The first field an entry names in backticks, as in "the mug in `photo`". */
21
+ function namedField(entry) {
22
+ if (typeof entry === 'string')
23
+ return /\x60([^\x60\s]+)\x60/.exec(entry)?.[1];
24
+ const entries = Array.isArray(entry) ? entry : Object.values(entry);
25
+ for (const each of entries) {
26
+ const field = namedField(each);
27
+ if (field !== undefined)
28
+ return field;
29
+ }
30
+ return undefined;
31
+ }
32
+ /** The page's view of a decision profile, the questions its host asks, and the host's own images. */
33
+ export function decisionInterface(profile, questions, images) {
34
+ return {
35
+ type: 'decision',
36
+ identity: { handle: profile.identity.handle },
37
+ contract: profile.decision.contract,
38
+ ...(profile.inputs.maxStateBytes === undefined
39
+ ? {}
40
+ : { maxStateBytes: profile.inputs.maxStateBytes }),
41
+ questions: Object.entries(questions).map(([id, question]) => ({
42
+ id,
43
+ type: question.type,
44
+ labels: question.type === 'choice'
45
+ ? Object.keys(question.criteria)
46
+ : question.type === 'score'
47
+ ? question.criteria.map((entry, level) => entryLabel(entry, String(level)))
48
+ : [],
49
+ ...(question.type === 'box'
50
+ ? withField(question.image ?? namedField(question.instructions))
51
+ : {}),
52
+ })),
53
+ ...(images && Object.keys(images).length > 0 ? { images } : {}),
54
+ ...(profile.observability
55
+ ? { observability: { record: resolveObservabilityPolicy(profile.observability).record } }
56
+ : {}),
57
+ };
58
+ }
59
+ /**
60
+ * The images a decision sends: the host's own and the page's. A page image may not take a name
61
+ * the host's use, so the questions always read the host's picture.
62
+ */
63
+ export function decisionAssets(host, page) {
64
+ const clash = Object.keys(page ?? {}).find((name) => host && Object.hasOwn(host, name));
65
+ if (clash !== undefined)
66
+ // labels-exempt: internal diagnostic; the user reads error.request
67
+ throw new TheoremError('request', `the image ${clash} is the host's, so the page's needs another name`);
68
+ const all = { ...host, ...page };
69
+ return Object.keys(all).length > 0 ? all : undefined;
70
+ }
71
+ const probability = z.number().min(0).max(1);
72
+ const probabilities = z
73
+ .record(z.string(), probability)
74
+ .refine((record) => Math.abs(Object.values(record).reduce((sum, value) => sum + value, 0) - 1) <= 0.001);
75
+ const answerSchema = z.discriminatedUnion('type', [
76
+ z.object({
77
+ type: z.literal('choice'),
78
+ choice: z.string(),
79
+ confidence: probability,
80
+ probabilities,
81
+ }),
82
+ z.object({ type: z.literal('noul'), noul: probability }),
83
+ z.object({
84
+ type: z.literal('box'),
85
+ box: z.object({ top: probability, bottom: probability, left: probability, right: probability }),
86
+ confidence: probability,
87
+ }),
88
+ z.object({
89
+ type: z.literal('score'),
90
+ score: z.number().nonnegative(),
91
+ confidence: probability,
92
+ legend: z.record(z.string(), z.string().min(1)),
93
+ probabilities,
94
+ }),
95
+ ]);
96
+ const replySchema = z.object({
97
+ result: z.object({
98
+ model: z.string().min(1),
99
+ answers: z.record(z.string(), answerSchema),
100
+ usage: z
101
+ .object({
102
+ inputTokens: z.number().int().nonnegative(),
103
+ outputTokens: z.number().int().nonnegative(),
104
+ costUsd: z.number().nonnegative().optional(),
105
+ })
106
+ .optional(),
107
+ }),
108
+ traces: z.array(traceRecordSchema).optional(),
109
+ });
110
+ /** The trace records a failed decision left, by the failure thrown for it. */
111
+ const traceFailures = new WeakMap();
112
+ /** Marks a decision's failure with the trace records it left, for the page's inspector. */
113
+ export function withFailedTraces(err, traces) {
114
+ if (traces.length > 0 && typeof err === 'object' && err !== null)
115
+ traceFailures.set(err, traces);
116
+ return err;
117
+ }
118
+ /** The trace records a failed decision left; none when its host sent none. */
119
+ export function failedTraces(err) {
120
+ return (typeof err === 'object' && err !== null && traceFailures.get(err)) || [];
121
+ }
122
+ export const failedDecisionTraces = failedTraces;
123
+ const failedBody = z.object({ traces: z.array(traceRecordSchema) });
124
+ /** POSTs `body` and reads its reply with `read`; a failure keeps the trace records its body carried. */
125
+ export async function postWithTraces(url, body, signal, options, read) {
126
+ let traces = [];
127
+ const failed = (raw) => {
128
+ const parsed = failedBody.safeParse(raw);
129
+ if (parsed.success)
130
+ traces = parsed.data.traces;
131
+ };
132
+ try {
133
+ return read(await fetchJson(url, { body, signal, failed }, options));
134
+ }
135
+ catch (err) {
136
+ throw withFailedTraces(err, traces);
137
+ }
138
+ }
139
+ /** POSTs a decision and reads its reply; a failure keeps the trace records its body carried. */
140
+ export function postDecision(url, body, signal, options = {}) {
141
+ return postWithTraces(url, body, signal, options, readDecisionReply);
142
+ }
143
+ /** A host's decide reply, checked: the answers, and any trace records it delivers. */
144
+ export function readDecisionReply(raw) {
145
+ // labels-exempt: internal diagnostic; the user reads error.bad_response
146
+ return checkWire(replySchema, raw, 'the decision');
147
+ }
148
+ const describedSchema = z.object({
149
+ interface: z.object({
150
+ type: z.literal('decision'),
151
+ identity: z.object({ handle: z.string() }),
152
+ contract: z.string(),
153
+ maxStateBytes: z.number().optional(),
154
+ questions: z.array(z.object({
155
+ id: z.string(),
156
+ type: z.enum(['choice', 'noul', 'score', 'box']),
157
+ labels: z.array(z.string()),
158
+ field: z.string().optional(),
159
+ })),
160
+ images: z
161
+ .record(z.string(), z.object({ mimeType: z.enum(['image/png', 'image/jpeg']), data: z.string() }))
162
+ .optional(),
163
+ observability: z.object({ record: z.boolean() }).optional(),
164
+ }),
165
+ });
166
+ /** Transport for a host mounted with `createTheoremDecisionHandler`. Default endpoint `/api/decision`. */
167
+ export function createDecisionTransport(options = {}) {
168
+ const base = (options.endpoint ?? '/api/decision').replace(/\/$/, '');
169
+ return {
170
+ async describe(signal) {
171
+ return checkWire(describedSchema, await fetchJson(base, { signal }, options), 'the decision description').interface;
172
+ },
173
+ decide: (state, signal, assets) => postDecision(`${base}/decide`, { state, ...(assets ? { assets } : {}) }, signal, options),
174
+ };
175
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Restore composer fields from a serializable pending `UserTurnDraft`.
3
+ */
4
+ import type { UserTurnDraft } from '@theoremjs/agents/interface';
5
+ export type ComposerDraftFields = {
6
+ text: string;
7
+ files: File[];
8
+ voice: File[];
9
+ };
10
+ /** Map an encoded pending draft back into live composer file fields. */
11
+ export declare function composerFieldsFromDraft(draft: UserTurnDraft): ComposerDraftFields;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Restore composer fields from a serializable pending `UserTurnDraft`.
3
+ */
4
+ import { pendingAttachmentsToFiles } from "./encode-files.js";
5
+ /** Map an encoded pending draft back into live composer file fields. */
6
+ export function composerFieldsFromDraft(draft) {
7
+ return {
8
+ text: draft.text ?? '',
9
+ files: pendingAttachmentsToFiles(draft.attachments ?? []),
10
+ voice: pendingAttachmentsToFiles(draft.voice ?? []),
11
+ };
12
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The rules behind an embedding console: the inputs as the JSON a request
3
+ * sends, text split into documents, documents packed into the requests the
4
+ * profile allows, all checked the way the kernel checks them, and documents
5
+ * ranked by how alike they point to a query.
6
+ *
7
+ * @module
8
+ */
9
+ import { type EmbeddingInput, type EmbeddingPart, type EmbeddingUsage } from '@theoremjs/agents';
10
+ import type { AttachmentValidationIssue } from '@theoremjs/agents/interface';
11
+ import type { EmbeddingInterface } from './embedding-transport.ts';
12
+ /** A part that is not text: a file sent inline, or one the provider already holds. */
13
+ export type EmbeddingMediaPart = Exclude<EmbeddingPart, {
14
+ type: 'text';
15
+ }>;
16
+ export type EmbeddingInputCheck = {
17
+ ok: true;
18
+ inputs: EmbeddingInput[];
19
+ } | {
20
+ ok: false;
21
+ reason: 'invalid_json' | 'no_inputs';
22
+ } | {
23
+ ok: false;
24
+ reason: 'too_many_inputs';
25
+ max: number;
26
+ inputs: EmbeddingInput[];
27
+ } | {
28
+ ok: false;
29
+ reason: 'empty_input' | 'text_off';
30
+ index: number;
31
+ inputs: EmbeddingInput[];
32
+ } | {
33
+ ok: false;
34
+ reason: 'attachments';
35
+ issues: AttachmentValidationIssue[];
36
+ inputs: EmbeddingInput[];
37
+ };
38
+ /** The request's inputs, read from its JSON; `undefined` when it is not a list of inputs. */
39
+ export declare function readEmbeddingInputs(text: string): EmbeddingInput[] | undefined;
40
+ /** One input, read from its JSON; `undefined` when it is not one. */
41
+ export declare function readEmbeddingInput(text: string): EmbeddingInput | undefined;
42
+ /** One input as fields edit it: its words, and the files beside them. */
43
+ export type EmbeddingFields = {
44
+ text: string;
45
+ media: EmbeddingMediaPart[];
46
+ };
47
+ /** The input as fields, or `null` when it holds more than one text part and fields would lose it. */
48
+ export declare function inputFields(input: EmbeddingInput): EmbeddingFields | null;
49
+ /** Fields as the input they send: plain text alone, else its parts with the text first. */
50
+ export declare function fieldsInput(fields: EmbeddingFields): EmbeddingInput;
51
+ /** The inputs, held to the profile's rules and its model's limits as the kernel holds them in one request. */
52
+ export declare function checkInputs(inputs: readonly EmbeddingInput[], rules: EmbeddingInterface['inputs']): EmbeddingInputCheck;
53
+ /** The request's JSON as the inputs it sends, held to the profile's rules. */
54
+ export declare function checkEmbeddingInputs(text: string, rules: EmbeddingInterface['inputs']): EmbeddingInputCheck;
55
+ /** Where text is cut into documents: not at all, at each line, at blank lines, at commas, or at a separator of your own. */
56
+ export type EmbeddingSplit = 'whole' | 'lines' | 'paragraphs' | 'commas' | {
57
+ separator: string;
58
+ };
59
+ /** The text as documents, each trimmed, none empty. */
60
+ export declare function splitText(text: string, split: EmbeddingSplit): string[];
61
+ /** A file read as words and split, not sent as media: plain text, Markdown, CSV, and JSON. */
62
+ export declare function readsAsText(file: {
63
+ name: string;
64
+ type: string;
65
+ }): boolean;
66
+ /** The most inputs the console sends in one request when the model sets no cap. */
67
+ export declare const MAX_BATCH_INPUTS = 100;
68
+ /**
69
+ * The inputs, in order, as the fewest requests the profile and its model
70
+ * allow: each holds at most `most` inputs and passes {@linkcode checkInputs}.
71
+ * An input refused on its own goes alone, for the kernel to refuse.
72
+ */
73
+ export declare function embeddingBatches(inputs: readonly EmbeddingInput[], rules: EmbeddingInterface['inputs'], most?: number): EmbeddingInput[][];
74
+ /** Two calls' usage as one: token counts and costs added, estimated when either was. */
75
+ export declare function addUsage(a: EmbeddingUsage | undefined, b: EmbeddingUsage | undefined): EmbeddingUsage | undefined;
76
+ /** How alike two vectors point, from -1 to 1. */
77
+ export declare function cosineSimilarity(a: readonly number[], b: readonly number[]): number;
78
+ /** A document and how alike it points to the query, from -1 to 1. */
79
+ export type EmbeddingMatch = {
80
+ index: number;
81
+ score: number;
82
+ };
83
+ /** The documents, most alike to the query first; a vector of another width is left out. */
84
+ export declare function rankMatches(query: readonly number[], documents: readonly (readonly number[])[]): EmbeddingMatch[];
@@ -0,0 +1,194 @@
1
+ /**
2
+ * The rules behind an embedding console: the inputs as the JSON a request
3
+ * sends, text split into documents, documents packed into the requests the
4
+ * profile allows, all checked the way the kernel checks them, and documents
5
+ * ranked by how alike they point to a query.
6
+ *
7
+ * @module
8
+ */
9
+ import { base64Bytes, embeddingMediaIssues, } from '@theoremjs/agents';
10
+ function isPart(value) {
11
+ if (typeof value !== 'object' || value === null)
12
+ return false;
13
+ const part = value;
14
+ if (part.type === 'text')
15
+ return typeof part.text === 'string';
16
+ if (typeof part.mimeType !== 'string')
17
+ return false;
18
+ if (part.type === 'media')
19
+ return typeof part.data === 'string';
20
+ return part.type === 'media-ref' && typeof part.uri === 'string';
21
+ }
22
+ function isInput(value) {
23
+ if (typeof value === 'string')
24
+ return true;
25
+ if (typeof value !== 'object' || value === null)
26
+ return false;
27
+ const parts = value.parts;
28
+ return Array.isArray(parts) && parts.every(isPart);
29
+ }
30
+ /** The request's inputs, read from its JSON; `undefined` when it is not a list of inputs. */
31
+ export function readEmbeddingInputs(text) {
32
+ let value;
33
+ try {
34
+ value = JSON.parse(text);
35
+ }
36
+ catch {
37
+ return undefined;
38
+ }
39
+ return Array.isArray(value) && value.every(isInput) ? value : undefined;
40
+ }
41
+ function partsOf(input) {
42
+ return typeof input === 'string' ? [{ type: 'text', text: input }] : input.parts;
43
+ }
44
+ /** One input, read from its JSON; `undefined` when it is not one. */
45
+ export function readEmbeddingInput(text) {
46
+ let value;
47
+ try {
48
+ value = JSON.parse(text);
49
+ }
50
+ catch {
51
+ return undefined;
52
+ }
53
+ return isInput(value) ? value : undefined;
54
+ }
55
+ /** The input as fields, or `null` when it holds more than one text part and fields would lose it. */
56
+ export function inputFields(input) {
57
+ const parts = partsOf(input);
58
+ const texts = parts.filter((part) => part.type === 'text');
59
+ if (texts.length > 1)
60
+ return null;
61
+ return {
62
+ text: texts[0]?.text ?? '',
63
+ media: parts.filter((part) => part.type !== 'text'),
64
+ };
65
+ }
66
+ /** Fields as the input they send: plain text alone, else its parts with the text first. */
67
+ export function fieldsInput(fields) {
68
+ if (fields.media.length === 0)
69
+ return fields.text;
70
+ return {
71
+ parts: [
72
+ ...(fields.text ? [{ type: 'text', text: fields.text }] : []),
73
+ ...fields.media,
74
+ ],
75
+ };
76
+ }
77
+ function mediaFacts(part) {
78
+ return part.type === 'media'
79
+ ? { mimeType: part.mimeType, sizeBytes: base64Bytes(part.data) }
80
+ : { mimeType: part.mimeType };
81
+ }
82
+ /** The inputs, held to the profile's rules and its model's limits as the kernel holds them in one request. */
83
+ export function checkInputs(inputs, rules) {
84
+ const list = [...inputs];
85
+ if (list.length === 0)
86
+ return { ok: false, reason: 'no_inputs' };
87
+ if (rules.maxInputs !== undefined && list.length > rules.maxInputs)
88
+ return { ok: false, reason: 'too_many_inputs', max: rules.maxInputs, inputs: list };
89
+ const media = [];
90
+ for (const [index, input] of list.entries()) {
91
+ const parts = partsOf(input);
92
+ if (parts.length === 0 || parts.every((part) => part.type === 'text' && !part.text.trim()))
93
+ return { ok: false, reason: 'empty_input', index, inputs: list };
94
+ if (!rules.text && parts.some((part) => part.type === 'text' && part.text.trim()))
95
+ return { ok: false, reason: 'text_off', index, inputs: list };
96
+ const files = [];
97
+ for (const part of parts) {
98
+ if (part.type === 'text')
99
+ continue;
100
+ try {
101
+ files.push(mediaFacts(part));
102
+ }
103
+ catch {
104
+ return { ok: false, reason: 'invalid_json' };
105
+ }
106
+ }
107
+ media.push(files);
108
+ }
109
+ const issues = embeddingMediaIssues(rules, media);
110
+ return issues.length > 0
111
+ ? { ok: false, reason: 'attachments', issues, inputs: list }
112
+ : { ok: true, inputs: list };
113
+ }
114
+ /** The request's JSON as the inputs it sends, held to the profile's rules. */
115
+ export function checkEmbeddingInputs(text, rules) {
116
+ const inputs = readEmbeddingInputs(text);
117
+ return inputs === undefined ? { ok: false, reason: 'invalid_json' } : checkInputs(inputs, rules);
118
+ }
119
+ /** The text as documents, each trimmed, none empty. */
120
+ export function splitText(text, split) {
121
+ const pieces = split === 'whole'
122
+ ? [text]
123
+ : split === 'lines'
124
+ ? text.split(/\r?\n/)
125
+ : split === 'paragraphs'
126
+ ? text.split(/\r?\n[^\S\r\n]*\r?\n/)
127
+ : split === 'commas'
128
+ ? text.split(',')
129
+ : split.separator
130
+ ? text.split(split.separator)
131
+ : [text];
132
+ return pieces.map((piece) => piece.trim()).filter((piece) => piece !== '');
133
+ }
134
+ /** A file read as words and split, not sent as media: plain text, Markdown, CSV, and JSON. */
135
+ export function readsAsText(file) {
136
+ return (file.type.startsWith('text/') ||
137
+ file.type === 'application/json' ||
138
+ /\.(txt|md|markdown|csv|tsv|json)$/i.test(file.name));
139
+ }
140
+ // why: OpenRouter caps no batch (it took 101 inputs); 100 is Gemini's cap, a size every provider checked carries.
141
+ /** The most inputs the console sends in one request when the model sets no cap. */
142
+ export const MAX_BATCH_INPUTS = 100;
143
+ /**
144
+ * The inputs, in order, as the fewest requests the profile and its model
145
+ * allow: each holds at most `most` inputs and passes {@linkcode checkInputs}.
146
+ * An input refused on its own goes alone, for the kernel to refuse.
147
+ */
148
+ export function embeddingBatches(inputs, rules, most = MAX_BATCH_INPUTS) {
149
+ const batches = [];
150
+ let current = [];
151
+ for (const input of inputs) {
152
+ const next = [...current, input];
153
+ if (current.length > 0 && (next.length > most || !checkInputs(next, rules).ok)) {
154
+ batches.push(current);
155
+ current = [input];
156
+ }
157
+ else
158
+ current = next;
159
+ }
160
+ if (current.length > 0)
161
+ batches.push(current);
162
+ return batches;
163
+ }
164
+ /** Two calls' usage as one: token counts and costs added, estimated when either was. */
165
+ export function addUsage(a, b) {
166
+ if (!a || !b)
167
+ return a ?? b;
168
+ const sum = (x, y) => x === undefined && y === undefined ? undefined : (x ?? 0) + (y ?? 0);
169
+ const inputTokens = sum(a.inputTokens, b.inputTokens);
170
+ const costUsd = sum(a.costUsd, b.costUsd);
171
+ return {
172
+ ...(inputTokens === undefined ? {} : { inputTokens }),
173
+ ...(costUsd === undefined ? {} : { costUsd }),
174
+ ...(a.estimated || b.estimated ? { estimated: true } : {}),
175
+ };
176
+ }
177
+ /** How alike two vectors point, from -1 to 1. */
178
+ export function cosineSimilarity(a, b) {
179
+ let dot = 0;
180
+ let aa = 0;
181
+ let bb = 0;
182
+ for (let i = 0; i < Math.min(a.length, b.length); i += 1) {
183
+ dot += a[i] * b[i];
184
+ aa += a[i] * a[i];
185
+ bb += b[i] * b[i];
186
+ }
187
+ return aa === 0 || bb === 0 ? 0 : dot / Math.sqrt(aa * bb);
188
+ }
189
+ /** The documents, most alike to the query first; a vector of another width is left out. */
190
+ export function rankMatches(query, documents) {
191
+ return documents
192
+ .flatMap((values, index) => values.length === query.length ? [{ index, score: cosineSimilarity(query, values) }] : [])
193
+ .sort((a, b) => b.score - a.score);
194
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * The index as a space: each document a point in a ball, placed by the
3
+ * three directions its vectors differ most along, each measured in its own
4
+ * spread so the space is as deep as it is wide. Its direction from the
5
+ * centre is its direction along those three; its distance is how far it sits
6
+ * from the index's mean along them, as `r / (r + median)`, scaled so the
7
+ * farthest document is on the rim: the order of distances is kept, and no
8
+ * one far document squeezes the rest into the centre. A pair is alike when
9
+ * its cosine similarity stands out against every other pair in the index:
10
+ * more than {@link OUTLIER} robust deviations above the median pair. Most
11
+ * pairs in an index are unrelated, so the median and its deviation say what
12
+ * unrelated looks like for this model and these documents. Each document is
13
+ * linked to its nearest neighbour, the one its cosine similarity is highest
14
+ * with, when that pair is alike. A cluster is documents alike pairs connect,
15
+ * directly or through each other. Positions are a projection and only
16
+ * approximate; links and clusters are the vectors' own cosine similarity.
17
+ *
18
+ * @module
19
+ */
20
+ /** A point in the space, inside the unit ball. */
21
+ export type SpacePoint = readonly [number, number, number];
22
+ /** One document in the space. */
23
+ export type SpaceDocument = {
24
+ /** Its index among the documents. */
25
+ readonly index: number;
26
+ /** Where it sits in the unit ball. */
27
+ readonly position: SpacePoint;
28
+ /** The cluster it belongs to. */
29
+ readonly cluster: number;
30
+ };
31
+ /** Documents alike pairs connect, directly or through each other. */
32
+ export type SpaceCluster = {
33
+ /** Its documents' indexes, in order. */
34
+ readonly members: readonly number[];
35
+ /** Its hue, from 0 to 1 around the colour wheel: where its members' mean vector lands, as an angle around the space's two widest directions, so neighbouring clusters share a family. */
36
+ readonly tone: number;
37
+ };
38
+ /** A document and its nearest neighbour, whose cosine similarity is above the space's threshold. */
39
+ export type SpaceLink = {
40
+ readonly a: number;
41
+ readonly b: number;
42
+ /** Their cosine similarity. */
43
+ readonly similarity: number;
44
+ };
45
+ /** The three directions the space is drawn along, kept so a query lands consistently. */
46
+ export type SpaceBasis = {
47
+ readonly mean: Float64Array;
48
+ /** Each a unit direction in the vectors' own space; fewer than three when the documents span fewer. */
49
+ readonly axes: readonly Float64Array[];
50
+ /** Each axis's spread over the documents; a point is measured along each in its own spread, so the space is as deep as it is wide. */
51
+ readonly spreads: readonly number[];
52
+ /** The documents' median distance from the mean, so measured. */
53
+ readonly scale: number;
54
+ /** The farthest document's `r / (r + scale)`; dividing by it puts that document on the rim. */
55
+ readonly reach: number;
56
+ };
57
+ export type EmbeddingSpace = {
58
+ readonly documents: readonly SpaceDocument[];
59
+ readonly clusters: readonly SpaceCluster[];
60
+ /** Each document's nearest neighbour, where that pair is above the threshold; a pair that is each other's nearest appears once. */
61
+ readonly links: readonly SpaceLink[];
62
+ /** The cosine similarity a pair must pass to be linked; `Infinity` when the index has too few pairs, or too alike, to say what unrelated looks like. */
63
+ readonly threshold: number;
64
+ readonly basis: SpaceBasis;
65
+ /** How wide the vectors are. */
66
+ readonly dimensions: number;
67
+ };
68
+ /**
69
+ * The similarity one must pass to stand out among `similarities`: their
70
+ * median plus {@link OUTLIER} robust deviations. `Infinity` when they have no
71
+ * spread, since then nothing stands out.
72
+ */
73
+ export declare function outlierThreshold(similarities: ArrayLike<number>): number;
74
+ /** The space for a set of document vectors, all at once. */
75
+ export declare function layoutSpace(vectors: readonly (readonly number[])[]): EmbeddingSpace;
76
+ /**
77
+ * The same space, laid out in slices between which the page goes on, so a
78
+ * large index never freezes it. Rejects with the signal's reason once aborted.
79
+ */
80
+ export declare function layoutSpaceSoon(vectors: readonly (readonly number[])[], signal?: AbortSignal): Promise<EmbeddingSpace>;
81
+ /** Where a query lands in the space: projected along the same directions as the documents. `null` when its width differs. */
82
+ export declare function placeQuery(space: EmbeddingSpace, query: readonly number[]): SpacePoint | null;