@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
@@ -6,7 +6,7 @@
6
6
  export const STASH_SHORTCUT = 'mod+shift+s';
7
7
  /** Whether a keydown is the stash shortcut (⌘⇧S / Ctrl+Shift+S). */
8
8
  export function isStashShortcut(event) {
9
- return event.code === 'KeyS' && event.shiftKey && !event.altKey && (event.metaKey || event.ctrlKey);
9
+ return (event.code === 'KeyS' && event.shiftKey && !event.altKey && (event.metaKey || event.ctrlKey));
10
10
  }
11
11
  /** The stash hint — shown while the whole draft is selected in the focused input and it can be stashed. */
12
12
  export function resolveComposerHint(args) {
@@ -0,0 +1,76 @@
1
+ import type { DecisionAsset, DecisionJson } from '@theoremjs/agents';
2
+ /**
3
+ * An image the page holds for a decision, by its reference: the text the state and the questions
4
+ * write where the image goes, such as `<pantry_shelf>`.
5
+ */
6
+ export type DecisionImages = Record<string, DecisionAsset>;
7
+ /** The image types a decision reads. */
8
+ export declare const IMAGE_ACCEPT = "image/png,image/jpeg";
9
+ /**
10
+ * A reference for a file that no image holds yet: its base name in angle brackets, then `_2`, `_3`,
11
+ * and so on. The model reads any text as a reference, so the brackets keep it from matching words.
12
+ */
13
+ export declare function imageName(file: string, taken: readonly string[]): string;
14
+ /** A file as an asset, or null when it is not a PNG or JPEG. */
15
+ export declare function readImage(file: File): Promise<DecisionAsset | null>;
16
+ /** The image as a `src` the page can show. */
17
+ export declare function imageSrc(image: DecisionAsset): string;
18
+ /** The held images the state still names, the only ones a decision sends. */
19
+ export declare function namedImages(images: DecisionImages, state: DecisionJson): DecisionImages;
20
+ /**
21
+ * The field a dropped file becomes: its reference, unused by any image or field, and the field
22
+ * named after it, such as `plant` holding `<plant>`.
23
+ */
24
+ export declare function imageField(file: string, images: DecisionImages, fields: readonly string[]): {
25
+ key: string;
26
+ reference: string;
27
+ };
28
+ /** The state as named fields, or undefined when it is a list, a value, or nothing. */
29
+ export declare function stateFields(state: DecisionJson | undefined): Record<string, DecisionJson> | undefined;
30
+ /**
31
+ * The images and state after files are dropped: each file a field named after it, holding a
32
+ * reference no image the page or the host holds, nor any field, has yet.
33
+ */
34
+ export declare function droppedImages(dropped: readonly (readonly [File, DecisionAsset])[], held: {
35
+ images: DecisionImages;
36
+ host?: DecisionImages;
37
+ state: Record<string, DecisionJson>;
38
+ }): {
39
+ images: DecisionImages;
40
+ state: Record<string, DecisionJson>;
41
+ };
42
+ /**
43
+ * The image a box answer is drawn on: `field` itself when it is a held reference, else the one at
44
+ * that state field, else the first the state refers to, else the first held. A box answer names no image, so this is the
45
+ * page's best reading of where it points.
46
+ */
47
+ export declare function boxSource(images: DecisionImages, state: DecisionJson, field?: string): DecisionAsset | undefined;
48
+ /** A box answer's edges, each a share of the image from its top or left. */
49
+ export type DecisionBox = {
50
+ top: number;
51
+ bottom: number;
52
+ left: number;
53
+ right: number;
54
+ };
55
+ /** The box in an image's pixels, and an outline that stays visible at any size. */
56
+ export declare function boxRect(box: DecisionBox, width: number, height: number): {
57
+ x: number;
58
+ y: number;
59
+ width: number;
60
+ height: number;
61
+ line: number;
62
+ };
63
+ /** The colours a marked image is drawn in: the box's outline, and the shade over the rest. */
64
+ export type BoxMarks = {
65
+ outline: string;
66
+ shade: string;
67
+ };
68
+ /** The whole image with the box outlined and the rest shaded, as a PNG `src`. */
69
+ export declare function markedBox(src: string, box: DecisionBox, marks: BoxMarks): Promise<string>;
70
+ /** The part of an image a box takes, as the frame's size and the image's place inside it, in percent. */
71
+ export declare function cropOf(box: DecisionBox): {
72
+ width: number;
73
+ height: number;
74
+ left: number;
75
+ top: number;
76
+ };
@@ -0,0 +1,139 @@
1
+ const MIME_TYPES = new Set(['image/png', 'image/jpeg']);
2
+ /** The image types a decision reads. */
3
+ export const IMAGE_ACCEPT = 'image/png,image/jpeg';
4
+ /**
5
+ * A reference for a file that no image holds yet: its base name in angle brackets, then `_2`, `_3`,
6
+ * and so on. The model reads any text as a reference, so the brackets keep it from matching words.
7
+ */
8
+ export function imageName(file, taken) {
9
+ const base = file
10
+ .replace(/\.[^.]*$/, '')
11
+ .toLowerCase()
12
+ .replace(/[^a-z0-9]+/g, '_')
13
+ .replace(/^_+|_+$/g, '') || 'image';
14
+ let name = `<${base}>`;
15
+ for (let n = 2; taken.includes(name); n += 1)
16
+ name = `<${base}_${String(n)}>`;
17
+ return name;
18
+ }
19
+ /** A file as an asset, or null when it is not a PNG or JPEG. */
20
+ export async function readImage(file) {
21
+ if (!MIME_TYPES.has(file.type))
22
+ return null;
23
+ const bytes = new Uint8Array(await file.arrayBuffer());
24
+ let binary = '';
25
+ for (let i = 0; i < bytes.length; i += 0x8000)
26
+ binary += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
27
+ return { mimeType: file.type, data: btoa(binary) };
28
+ }
29
+ /** The image as a `src` the page can show. */
30
+ export function imageSrc(image) {
31
+ return `data:${image.mimeType};base64,${image.data}`;
32
+ }
33
+ function mentions(value, name) {
34
+ if (typeof value === 'string')
35
+ return value === name;
36
+ if (Array.isArray(value))
37
+ return value.some((each) => mentions(each, name));
38
+ if (value !== null && typeof value === 'object')
39
+ return Object.values(value).some((each) => mentions(each, name));
40
+ return false;
41
+ }
42
+ /** The held images the state still names, the only ones a decision sends. */
43
+ export function namedImages(images, state) {
44
+ return Object.fromEntries(Object.entries(images).filter(([name]) => mentions(state, name)));
45
+ }
46
+ /**
47
+ * The field a dropped file becomes: its reference, unused by any image or field, and the field
48
+ * named after it, such as `plant` holding `<plant>`.
49
+ */
50
+ export function imageField(file, images, fields) {
51
+ const reference = imageName(file, [...Object.keys(images), ...fields.map((key) => `<${key}>`)]);
52
+ return { key: reference.slice(1, -1), reference };
53
+ }
54
+ /** The state as named fields, or undefined when it is a list, a value, or nothing. */
55
+ export function stateFields(state) {
56
+ return state !== null && typeof state === 'object' && !Array.isArray(state) ? state : undefined;
57
+ }
58
+ /**
59
+ * The images and state after files are dropped: each file a field named after it, holding a
60
+ * reference no image the page or the host holds, nor any field, has yet.
61
+ */
62
+ export function droppedImages(dropped, held) {
63
+ let { images, state } = held;
64
+ for (const [file, image] of dropped) {
65
+ const { key, reference } = imageField(file.name, { ...held.host, ...images }, Object.keys(state));
66
+ images = { ...images, [reference]: image };
67
+ state = { ...state, [key]: reference };
68
+ }
69
+ return { images, state };
70
+ }
71
+ function at(state, path) {
72
+ let value = state;
73
+ for (const step of path.split('.')) {
74
+ if (value === null || typeof value !== 'object')
75
+ return undefined;
76
+ value = Array.isArray(value) ? value[Number(step)] : value[step];
77
+ }
78
+ return value;
79
+ }
80
+ /**
81
+ * The image a box answer is drawn on: `field` itself when it is a held reference, else the one at
82
+ * that state field, else the first the state refers to, else the first held. A box answer names no image, so this is the
83
+ * page's best reading of where it points.
84
+ */
85
+ export function boxSource(images, state, field) {
86
+ const names = Object.keys(images);
87
+ const pointed = field === undefined || field in images ? field : at(state, field);
88
+ const named = (typeof pointed === 'string' && pointed in images ? pointed : undefined) ??
89
+ names.find((name) => mentions(state, name));
90
+ return images[named ?? names[0] ?? ''];
91
+ }
92
+ /** The box in an image's pixels, and an outline that stays visible at any size. */
93
+ export function boxRect(box, width, height) {
94
+ return {
95
+ x: box.left * width,
96
+ y: box.top * height,
97
+ width: (box.right - box.left) * width,
98
+ height: (box.bottom - box.top) * height,
99
+ line: Math.max(2, Math.round(Math.min(width, height) / 200)),
100
+ };
101
+ }
102
+ /** How much of the shade's colour lies over the rest of the image, so the image still shows through it. */
103
+ const SHADE_ALPHA = 0.45;
104
+ /** The whole image with the box outlined and the rest shaded, as a PNG `src`. */
105
+ export async function markedBox(src, box, marks) {
106
+ const image = new Image();
107
+ image.src = src;
108
+ await image.decode();
109
+ const canvas = document.createElement('canvas');
110
+ canvas.width = image.naturalWidth;
111
+ canvas.height = image.naturalHeight;
112
+ const context = canvas.getContext('2d');
113
+ if (!context)
114
+ return src;
115
+ const rect = boxRect(box, canvas.width, canvas.height);
116
+ context.drawImage(image, 0, 0);
117
+ context.fillStyle = marks.shade;
118
+ context.globalAlpha = SHADE_ALPHA;
119
+ context.beginPath();
120
+ context.rect(0, 0, canvas.width, canvas.height);
121
+ context.rect(rect.x, rect.y, rect.width, rect.height);
122
+ context.fill('evenodd');
123
+ context.globalAlpha = 1;
124
+ context.strokeStyle = marks.outline;
125
+ context.lineWidth = rect.line;
126
+ context.strokeRect(rect.x, rect.y, rect.width, rect.height);
127
+ return canvas.toDataURL('image/png');
128
+ }
129
+ /** The part of an image a box takes, as the frame's size and the image's place inside it, in percent. */
130
+ export function cropOf(box) {
131
+ const width = box.right - box.left;
132
+ const height = box.bottom - box.top;
133
+ return {
134
+ width: 100 / width,
135
+ height: 100 / height,
136
+ left: (-box.left / width) * 100,
137
+ top: (-box.top / height) * 100,
138
+ };
139
+ }
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * @module
6
6
  */
7
- import { type DecisionJson, type DecisionProfile, type DecisionQuestion, type DecisionResult, type TraceRecord } from '@theoremjs/agents';
7
+ import { type DecisionAsset, type DecisionJson, type DecisionProfile, type DecisionQuestion, type DecisionResult, type TraceRecord } from '@theoremjs/agents';
8
8
  import type { TraceFeed } from './trace-feed.ts';
9
9
  import { type HttpOptions } from './transport.ts';
10
10
  /** One question as the page sees it: its answer type and the names of its options or levels. Instructions stay on the host. */
@@ -13,6 +13,11 @@ export type DecisionQuestionView = {
13
13
  type: DecisionQuestion['type'];
14
14
  /** A choice's labels, a score's levels from 0 up; empty for a number. */
15
15
  labels: string[];
16
+ /**
17
+ * A box's image: the one its question gives, a reference or a state field, else the field its
18
+ * instructions first name in backticks.
19
+ */
20
+ field?: string;
16
21
  };
17
22
  /** What a decision profile shows the page. */
18
23
  export type DecisionInterface = {
@@ -23,12 +28,19 @@ export type DecisionInterface = {
23
28
  contract: string;
24
29
  maxStateBytes?: number;
25
30
  questions: DecisionQuestionView[];
31
+ /** The host's own images, by the reference its questions write, sent with every decision. */
32
+ images?: Record<string, DecisionAsset>;
26
33
  observability?: {
27
34
  record: boolean;
28
35
  };
29
36
  };
30
- /** The page's view of a decision profile and the questions its host asks. */
31
- export declare function decisionInterface(profile: Pick<DecisionProfile, 'identity' | 'decision' | 'inputs' | 'observability'>, questions: Record<string, DecisionQuestion>): DecisionInterface;
37
+ /** The page's view of a decision profile, the questions its host asks, and the host's own images. */
38
+ export declare function decisionInterface(profile: Pick<DecisionProfile, 'identity' | 'decision' | 'inputs' | 'observability'>, questions: Record<string, DecisionQuestion>, images?: Record<string, DecisionAsset>): DecisionInterface;
39
+ /**
40
+ * The images a decision sends: the host's own and the page's. A page image may not take a name
41
+ * the host's use, so the questions always read the host's picture.
42
+ */
43
+ export declare function decisionAssets(host: Record<string, DecisionAsset> | undefined, page: Record<string, DecisionAsset> | undefined): Record<string, DecisionAsset> | undefined;
32
44
  /** A decision's reply: the answers, and the trace records a host that delivers them sent back. */
33
45
  export type DecisionReply = {
34
46
  result: DecisionResult;
@@ -36,10 +48,20 @@ export type DecisionReply = {
36
48
  };
37
49
  export interface DecisionTransport {
38
50
  describe(signal?: AbortSignal): Promise<DecisionInterface>;
39
- decide(state: Exclude<DecisionJson, null>, signal?: AbortSignal): Promise<DecisionReply>;
51
+ /** `assets` are the images the state names, by reference. */
52
+ decide(state: Exclude<DecisionJson, null>, signal?: AbortSignal, assets?: Record<string, DecisionAsset>): Promise<DecisionReply>;
40
53
  /** Where the host's trace records land, for the inspector. */
41
54
  traces?: TraceFeed;
42
55
  }
56
+ /** Marks a decision's failure with the trace records it left, for the page's inspector. */
57
+ export declare function withFailedTraces(err: unknown, traces: readonly TraceRecord[]): unknown;
58
+ /** The trace records a failed decision left; none when its host sent none. */
59
+ export declare function failedTraces(err: unknown): readonly TraceRecord[];
60
+ export declare const failedDecisionTraces: typeof failedTraces;
61
+ /** POSTs `body` and reads its reply with `read`; a failure keeps the trace records its body carried. */
62
+ export declare function postWithTraces<Reply>(url: string, body: unknown, signal: AbortSignal | undefined, options: HttpOptions, read: (raw: unknown) => Reply): Promise<Reply>;
63
+ /** POSTs a decision and reads its reply; a failure keeps the trace records its body carried. */
64
+ export declare function postDecision(url: string, body: unknown, signal: AbortSignal | undefined, options?: HttpOptions): Promise<DecisionReply>;
43
65
  /** A host's decide reply, checked: the answers, and any trace records it delivers. */
44
66
  export declare function readDecisionReply(raw: unknown): DecisionReply;
45
67
  /** Transport for a host mounted with `createTheoremDecisionHandler`. Default endpoint `/api/decision`. */
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * @module
6
6
  */
7
- import { resolveObservabilityPolicy, traceRecordSchema, z, } from '@theoremjs/agents';
7
+ import { resolveObservabilityPolicy, TheoremError, traceRecordSchema, z, } from '@theoremjs/agents';
8
8
  import { fetchJson } from "./transport.js";
9
9
  import { checkWire } from "./wire-line.js";
10
10
  /** A score level's name: its text, or the first text inside a structured entry. */
@@ -14,8 +14,23 @@ function entryLabel(entry, fallback) {
14
14
  const entries = Array.isArray(entry) ? entry : Object.keys(entry);
15
15
  return entries.length ? entryLabel(entries[0], fallback) : fallback;
16
16
  }
17
- /** The page's view of a decision profile and the questions its host asks. */
18
- export function decisionInterface(profile, questions) {
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) {
19
34
  return {
20
35
  type: 'decision',
21
36
  identity: { handle: profile.identity.handle },
@@ -31,12 +46,28 @@ export function decisionInterface(profile, questions) {
31
46
  : question.type === 'score'
32
47
  ? question.criteria.map((entry, level) => entryLabel(entry, String(level)))
33
48
  : [],
49
+ ...(question.type === 'box'
50
+ ? withField(question.image ?? namedField(question.instructions))
51
+ : {}),
34
52
  })),
53
+ ...(images && Object.keys(images).length > 0 ? { images } : {}),
35
54
  ...(profile.observability
36
55
  ? { observability: { record: resolveObservabilityPolicy(profile.observability).record } }
37
56
  : {}),
38
57
  };
39
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
+ }
40
71
  const probability = z.number().min(0).max(1);
41
72
  const probabilities = z
42
73
  .record(z.string(), probability)
@@ -49,6 +80,11 @@ const answerSchema = z.discriminatedUnion('type', [
49
80
  probabilities,
50
81
  }),
51
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
+ }),
52
88
  z.object({
53
89
  type: z.literal('score'),
54
90
  score: z.number().nonnegative(),
@@ -71,9 +107,42 @@ const replySchema = z.object({
71
107
  }),
72
108
  traces: z.array(traceRecordSchema).optional(),
73
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
+ }
74
143
  /** A host's decide reply, checked: the answers, and any trace records it delivers. */
75
144
  export function readDecisionReply(raw) {
76
- // lexicon-exempt: internal diagnostic; the user reads error.bad_response
145
+ // labels-exempt: internal diagnostic; the user reads error.bad_response
77
146
  return checkWire(replySchema, raw, 'the decision');
78
147
  }
79
148
  const describedSchema = z.object({
@@ -84,9 +153,13 @@ const describedSchema = z.object({
84
153
  maxStateBytes: z.number().optional(),
85
154
  questions: z.array(z.object({
86
155
  id: z.string(),
87
- type: z.enum(['choice', 'noul', 'score']),
156
+ type: z.enum(['choice', 'noul', 'score', 'box']),
88
157
  labels: z.array(z.string()),
158
+ field: z.string().optional(),
89
159
  })),
160
+ images: z
161
+ .record(z.string(), z.object({ mimeType: z.enum(['image/png', 'image/jpeg']), data: z.string() }))
162
+ .optional(),
90
163
  observability: z.object({ record: z.boolean() }).optional(),
91
164
  }),
92
165
  });
@@ -97,6 +170,6 @@ export function createDecisionTransport(options = {}) {
97
170
  async describe(signal) {
98
171
  return checkWire(describedSchema, await fetchJson(base, { signal }, options), 'the decision description').interface;
99
172
  },
100
- decide: async (state, signal) => readDecisionReply(await fetchJson(`${base}/decide`, { body: { state }, signal }, options)),
173
+ decide: (state, signal, assets) => postDecision(`${base}/decide`, { state, ...(assets ? { assets } : {}) }, signal, options),
101
174
  };
102
175
  }
@@ -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[];