@theoremjs/web 0.0.0-stage → 0.3.4-next.375.ga2d1878

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 (128) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +493 -2
  3. package/client/audio-level.d.ts +5 -0
  4. package/client/audio-level.js +24 -0
  5. package/client/chat.d.ts +144 -0
  6. package/client/chat.js +760 -0
  7. package/client/composer-drawer.d.ts +19 -0
  8. package/client/composer-drawer.js +15 -0
  9. package/client/composer-hints.d.ts +25 -0
  10. package/client/composer-hints.js +20 -0
  11. package/client/composer-primary.d.ts +18 -0
  12. package/client/composer-primary.js +29 -0
  13. package/client/decision-transport.d.ts +70 -0
  14. package/client/decision-transport.js +175 -0
  15. package/client/decode-composer-draft.d.ts +11 -0
  16. package/client/decode-composer-draft.js +12 -0
  17. package/client/embedding-input.d.ts +84 -0
  18. package/client/embedding-input.js +194 -0
  19. package/client/embedding-space.d.ts +82 -0
  20. package/client/embedding-space.js +389 -0
  21. package/client/embedding-transport.d.ts +40 -0
  22. package/client/embedding-transport.js +71 -0
  23. package/client/encode-composer-draft.d.ts +9 -0
  24. package/client/encode-composer-draft.js +34 -0
  25. package/client/encode-files.d.ts +18 -0
  26. package/client/encode-files.js +54 -0
  27. package/client/failure.d.ts +29 -0
  28. package/client/failure.js +34 -0
  29. package/client/generation-selection.d.ts +15 -0
  30. package/client/generation-selection.js +24 -0
  31. package/client/host-transport.d.ts +70 -0
  32. package/client/host-transport.js +75 -0
  33. package/client/index.d.ts +37 -0
  34. package/client/index.js +31 -0
  35. package/client/live/live-mic-forward.d.ts +30 -0
  36. package/client/live/live-mic-forward.js +46 -0
  37. package/client/live/live-page-tool.d.ts +39 -0
  38. package/client/live/live-page-tool.js +26 -0
  39. package/client/live/live-state.d.ts +13 -0
  40. package/client/live/live-state.js +22 -0
  41. package/client/live/live-tool.d.ts +11 -0
  42. package/client/live/live-tool.js +1 -0
  43. package/client/live-client.d.ts +189 -0
  44. package/client/live-client.js +829 -0
  45. package/client/live-errors.d.ts +2 -0
  46. package/client/live-errors.js +9 -0
  47. package/client/live-messages.d.ts +148 -0
  48. package/client/live-messages.js +100 -0
  49. package/client/mic-capture.d.ts +4 -0
  50. package/client/mic-capture.js +6 -0
  51. package/client/oauth-popup.d.ts +26 -0
  52. package/client/oauth-popup.js +30 -0
  53. package/client/page-dispatch.d.ts +15 -0
  54. package/client/page-dispatch.js +63 -0
  55. package/client/pcm-downsample.d.ts +4 -0
  56. package/client/pcm-downsample.js +45 -0
  57. package/client/run-commit.d.ts +34 -0
  58. package/client/run-commit.js +49 -0
  59. package/client/run-session.d.ts +79 -0
  60. package/client/run-session.js +207 -0
  61. package/client/schema-fields.d.ts +63 -0
  62. package/client/schema-fields.js +199 -0
  63. package/client/shaped-data.d.ts +118 -0
  64. package/client/shaped-data.js +343 -0
  65. package/client/source-citations.d.ts +13 -0
  66. package/client/source-citations.js +56 -0
  67. package/client/space-geometry.d.ts +39 -0
  68. package/client/space-geometry.js +183 -0
  69. package/client/tool-result.d.ts +78 -0
  70. package/client/tool-result.js +293 -0
  71. package/client/tool-resume.d.ts +50 -0
  72. package/client/tool-resume.js +14 -0
  73. package/client/trace-feed.d.ts +13 -0
  74. package/client/trace-feed.js +18 -0
  75. package/client/transcript-schema.d.ts +10 -0
  76. package/client/transcript-schema.js +120 -0
  77. package/client/transport.d.ts +264 -0
  78. package/client/transport.js +330 -0
  79. package/client/turn-client.d.ts +44 -0
  80. package/client/turn-client.js +88 -0
  81. package/client/wire-line.d.ts +56 -0
  82. package/client/wire-line.js +90 -0
  83. package/client/worklets/mic-capture.js +16 -0
  84. package/package.json +28 -4
  85. package/server/checkpoint-ownership.d.ts +6 -0
  86. package/server/checkpoint-ownership.js +42 -0
  87. package/server/conversation-store.d.ts +92 -0
  88. package/server/conversation-store.js +131 -0
  89. package/server/conversation.d.ts +193 -0
  90. package/server/conversation.js +552 -0
  91. package/server/credential-store.d.ts +24 -0
  92. package/server/credential-store.js +14 -0
  93. package/server/decision-handler.d.ts +42 -0
  94. package/server/decision-handler.js +46 -0
  95. package/server/embedding-handler.d.ts +33 -0
  96. package/server/embedding-handler.js +45 -0
  97. package/server/handler.d.ts +158 -0
  98. package/server/handler.js +775 -0
  99. package/server/json-response.d.ts +2 -0
  100. package/server/json-response.js +7 -0
  101. package/server/mcp-handler.d.ts +46 -0
  102. package/server/mcp-handler.js +312 -0
  103. package/server/mod.d.ts +27 -0
  104. package/server/mod.js +25 -0
  105. package/server/page-dispatch.d.ts +22 -0
  106. package/server/page-dispatch.js +127 -0
  107. package/server/request-body.d.ts +2 -0
  108. package/server/request-body.js +42 -0
  109. package/server/request-check.d.ts +21 -0
  110. package/server/request-check.js +49 -0
  111. package/server/session-authority.d.ts +8 -0
  112. package/server/session-authority.js +78 -0
  113. package/server/session-boundary.d.ts +26 -0
  114. package/server/session-boundary.js +64 -0
  115. package/server/session-identity.d.ts +16 -0
  116. package/server/session-identity.js +41 -0
  117. package/server/session-limits.d.ts +12 -0
  118. package/server/session-limits.js +59 -0
  119. package/server/session-store.d.ts +123 -0
  120. package/server/session-store.js +264 -0
  121. package/server/single-action.d.ts +21 -0
  122. package/server/single-action.js +30 -0
  123. package/server/steer-inbox.d.ts +35 -0
  124. package/server/steer-inbox.js +52 -0
  125. package/server/tool-operations.d.ts +6 -0
  126. package/server/tool-operations.js +132 -0
  127. package/server/turn-input.d.ts +10 -0
  128. package/server/turn-input.js +29 -0
@@ -0,0 +1,19 @@
1
+ import type { ComposerPendingMessage } from '@theoremjs/agents/interface';
2
+ /** What waits in the composer, by kind; the UI words each kind. */
3
+ export type ComposerDrawerKind = 'steer' | 'queue' | 'stash' | 'attached';
4
+ /** Collapsed-drawer header: the total, then each waiting kind's count, in display order. */
5
+ export type ComposerDrawerSummary = {
6
+ count: number;
7
+ parts: {
8
+ kind: ComposerDrawerKind;
9
+ n: number;
10
+ }[];
11
+ };
12
+ /**
13
+ * What's waiting in the composer, by kind: steering, queued, stashed, attached
14
+ * (files and voice notes). Null when nothing waits.
15
+ */
16
+ export declare function composerDrawerSummary(args: {
17
+ pendingMessages: readonly Pick<ComposerPendingMessage, 'kind'>[];
18
+ attachmentCount: number;
19
+ }): ComposerDrawerSummary | null;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * What's waiting in the composer, by kind: steering, queued, stashed, attached
3
+ * (files and voice notes). Null when nothing waits.
4
+ */
5
+ export function composerDrawerSummary(args) {
6
+ const countOf = (kind) => args.pendingMessages.filter((message) => message.kind === kind).length;
7
+ const parts = [
8
+ { kind: 'steer', n: countOf('steer') },
9
+ { kind: 'queue', n: countOf('queue') },
10
+ { kind: 'stash', n: countOf('stash') },
11
+ { kind: 'attached', n: args.attachmentCount },
12
+ ].filter((part) => part.n > 0);
13
+ const count = parts.reduce((sum, part) => sum + part.n, 0);
14
+ return count === 0 ? null : { count, parts };
15
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Composer hints — ephemeral guidance about the current composer interaction
3
+ * (not pending-message state). Mirrors seance's `stash-selected-draft` hint.
4
+ */
5
+ /** Stash shortcut in Astryx `Kbd` notation (`mod` is ⌘ on macOS, Ctrl elsewhere). */
6
+ export declare const STASH_SHORTCUT = "mod+shift+s";
7
+ /** Which hint applies, and its shortcut; the UI words it. */
8
+ export type ComposerHint = {
9
+ id: 'stash-selected-draft';
10
+ shortcut: string;
11
+ };
12
+ /** Whether a keydown is the stash shortcut (⌘⇧S / Ctrl+Shift+S). */
13
+ export declare function isStashShortcut(event: {
14
+ code: string;
15
+ metaKey: boolean;
16
+ ctrlKey: boolean;
17
+ shiftKey: boolean;
18
+ altKey: boolean;
19
+ }): boolean;
20
+ /** The stash hint — shown while the whole draft is selected in the focused input and it can be stashed. */
21
+ export declare function resolveComposerHint(args: {
22
+ draftText: string;
23
+ selectedText: string;
24
+ canStash: boolean;
25
+ }): ComposerHint | null;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Composer hints — ephemeral guidance about the current composer interaction
3
+ * (not pending-message state). Mirrors seance's `stash-selected-draft` hint.
4
+ */
5
+ /** Stash shortcut in Astryx `Kbd` notation (`mod` is ⌘ on macOS, Ctrl elsewhere). */
6
+ export const STASH_SHORTCUT = 'mod+shift+s';
7
+ /** Whether a keydown is the stash shortcut (⌘⇧S / Ctrl+Shift+S). */
8
+ export function isStashShortcut(event) {
9
+ return (event.code === 'KeyS' && event.shiftKey && !event.altKey && (event.metaKey || event.ctrlKey));
10
+ }
11
+ /** The stash hint — shown while the whole draft is selected in the focused input and it can be stashed. */
12
+ export function resolveComposerHint(args) {
13
+ const draft = args.draftText.trim();
14
+ if (!args.canStash || !draft || args.selectedText.trim() !== draft)
15
+ return null;
16
+ return {
17
+ id: 'stash-selected-draft',
18
+ shortcut: STASH_SHORTCUT,
19
+ };
20
+ }
@@ -0,0 +1,18 @@
1
+ import { type ComposerMenuAction, type ComposerPrimaryAction, type ComposerProfileInterface, type ComposerRunPhase } from '@theoremjs/agents/interface';
2
+ /** Whether the composer holds something to send: text that isn't blank, a file, or a voice note. */
3
+ export declare function composerHasPayload(draftText: string, pendingFiles: readonly File[], pendingVoice: readonly File[]): boolean;
4
+ export type ComposerActionState = {
5
+ primary: ComposerPrimaryAction;
6
+ menuActions: ComposerMenuAction[];
7
+ /** Send is off while recording, with nothing to act on, or with an empty draft to send or queue. */
8
+ primaryDisabled: boolean;
9
+ };
10
+ /** The composer's primary button and send menu for the current draft and run phase. */
11
+ export declare function composerActionState(args: {
12
+ iface: ComposerProfileInterface;
13
+ phase: ComposerRunPhase;
14
+ draftText: string;
15
+ pendingFiles: readonly File[];
16
+ pendingVoice: readonly File[];
17
+ recording: boolean;
18
+ }): ComposerActionState;
@@ -0,0 +1,29 @@
1
+ import { resolveComposerMenuActions, resolveComposerPrimary, userDraftHasPayload, } from '@theoremjs/agents/interface';
2
+ function fileSpecs(files) {
3
+ return files.map((file) => ({
4
+ name: file.name,
5
+ mimeType: file.type || 'application/octet-stream',
6
+ sizeBytes: file.size,
7
+ }));
8
+ }
9
+ /** Whether the composer holds something to send: text that isn't blank, a file, or a voice note. */
10
+ export function composerHasPayload(draftText, pendingFiles, pendingVoice) {
11
+ return userDraftHasPayload({
12
+ ...(draftText.trim() ? { text: draftText } : {}),
13
+ ...(pendingFiles.length ? { attachments: fileSpecs(pendingFiles) } : {}),
14
+ ...(pendingVoice.length ? { voice: fileSpecs(pendingVoice) } : {}),
15
+ });
16
+ }
17
+ /** The composer's primary button and send menu for the current draft and run phase. */
18
+ export function composerActionState(args) {
19
+ const allowSteering = 'allowSteering' in args.iface && Boolean(args.iface.allowSteering);
20
+ const hasPayload = composerHasPayload(args.draftText, args.pendingFiles, args.pendingVoice);
21
+ const context = { phase: args.phase, hasPayload, allowSteering };
22
+ const primary = resolveComposerPrimary(context);
23
+ const needsPayload = primary === 'send' || primary === 'queue';
24
+ return {
25
+ primary,
26
+ menuActions: resolveComposerMenuActions(context),
27
+ primaryDisabled: args.recording || primary === 'none' || (needsPayload && !hasPayload),
28
+ };
29
+ }
@@ -0,0 +1,70 @@
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 { type DecisionAsset, type DecisionJson, type DecisionProfile, type DecisionQuestion, type DecisionResult, type TraceRecord } from '@theoremjs/agents';
8
+ import type { TraceFeed } from './trace-feed.ts';
9
+ import { type HttpOptions } from './transport.ts';
10
+ /** One question as the page sees it: its answer type and the names of its options or levels. Instructions stay on the host. */
11
+ export type DecisionQuestionView = {
12
+ id: string;
13
+ type: DecisionQuestion['type'];
14
+ /** A choice's labels, a score's levels from 0 up; empty for a number. */
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;
21
+ };
22
+ /** What a decision profile shows the page. */
23
+ export type DecisionInterface = {
24
+ type: 'decision';
25
+ identity: {
26
+ handle: string;
27
+ };
28
+ contract: string;
29
+ maxStateBytes?: number;
30
+ questions: DecisionQuestionView[];
31
+ /** The host's own images, by the reference its questions write, sent with every decision. */
32
+ images?: Record<string, DecisionAsset>;
33
+ observability?: {
34
+ record: boolean;
35
+ };
36
+ };
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;
44
+ /** A decision's reply: the answers, and the trace records a host that delivers them sent back. */
45
+ export type DecisionReply = {
46
+ result: DecisionResult;
47
+ traces?: TraceRecord[];
48
+ };
49
+ export interface DecisionTransport {
50
+ describe(signal?: AbortSignal): Promise<DecisionInterface>;
51
+ /** `assets` are the images the state names, by reference. */
52
+ decide(state: Exclude<DecisionJson, null>, signal?: AbortSignal, assets?: Record<string, DecisionAsset>): Promise<DecisionReply>;
53
+ /** Where the host's trace records land, for the inspector. */
54
+ traces?: TraceFeed;
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>;
65
+ /** A host's decide reply, checked: the answers, and any trace records it delivers. */
66
+ export declare function readDecisionReply(raw: unknown): DecisionReply;
67
+ /** Transport for a host mounted with `createTheoremDecisionHandler`. Default endpoint `/api/decision`. */
68
+ export declare function createDecisionTransport(options?: HttpOptions & {
69
+ endpoint?: string;
70
+ }): DecisionTransport;
@@ -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[];