@theoremjs/react 0.3.4-next.363.g11be542 → 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 (201) hide show
  1. package/README.md +23 -336
  2. package/client/host-call.d.ts +2 -2
  3. package/client/host-call.js +1 -2
  4. package/client/index.d.ts +1 -28
  5. package/client/index.js +1 -22
  6. package/client/live/apply-live-turn-tool-event.js +1 -1
  7. package/client/live/run-live-tool-call.d.ts +1 -3
  8. package/client/live/run-live-tool-call.js +1 -1
  9. package/client/transcript-groups.js +1 -1
  10. package/client/voice-recorder.js +1 -1
  11. package/components/live/use-live-runner-controls.d.ts +1 -2
  12. package/components/live/use-live-runner-model.d.ts +7 -8
  13. package/components/live/use-live-runner-model.js +1 -2
  14. package/components/live/use-live-runner-ui.d.ts +2 -4
  15. package/components/live/use-live-runner-ui.js +1 -1
  16. package/components/live/use-live-session-client.d.ts +2 -5
  17. package/components/live/use-live-session-client.js +1 -1
  18. package/hooks/use-described.d.ts +1 -1
  19. package/hooks/use-described.js +1 -1
  20. package/hooks/use-embedding-index.d.ts +1 -1
  21. package/hooks/use-embedding-index.js +1 -1
  22. package/hooks/use-one-call.d.ts +2 -2
  23. package/hooks/use-one-call.js +1 -2
  24. package/hooks/use-page-tool-warnings.d.ts +2 -5
  25. package/hooks/use-page-tool-warnings.js +3 -14
  26. package/hooks/use-theorem-chat.d.ts +35 -81
  27. package/hooks/use-theorem-chat.js +30 -522
  28. package/hooks/use-theorem-decision.d.ts +1 -2
  29. package/hooks/use-theorem-embedding.d.ts +1 -2
  30. package/hooks/use-theorem-host.d.ts +2 -4
  31. package/hooks/use-theorem-host.js +1 -4
  32. package/hooks/use-theorem-interface.d.ts +1 -2
  33. package/hooks/use-trace-records.d.ts +1 -1
  34. package/index.d.ts +2 -3
  35. package/index.js +2 -3
  36. package/live.d.ts +1 -2
  37. package/package.json +3 -2
  38. package/server/mod.d.ts +1 -26
  39. package/server/mod.js +1 -24
  40. package/ui/ChatComposerBar.d.ts +1 -1
  41. package/ui/ChatComposerBar.js +1 -3
  42. package/ui/ChatTranscript.d.ts +1 -1
  43. package/ui/ChatTranscript.js +1 -1
  44. package/ui/ConsoleFrame.d.ts +1 -1
  45. package/ui/DecisionAnswers.d.ts +1 -1
  46. package/ui/EmbeddingDocuments.d.ts +2 -2
  47. package/ui/EmbeddingDocuments.js +1 -2
  48. package/ui/EmbeddingQuery.d.ts +1 -1
  49. package/ui/EmbeddingQuery.js +1 -2
  50. package/ui/EmbeddingResults.d.ts +1 -1
  51. package/ui/EmbeddingResults.js +1 -1
  52. package/ui/EmbeddingSpace.d.ts +1 -2
  53. package/ui/LiveRunner.d.ts +1 -2
  54. package/ui/SchemaFields.d.ts +1 -1
  55. package/ui/SchemaFields.js +1 -2
  56. package/ui/ShapedData.js +1 -1
  57. package/ui/StateFields.js +1 -1
  58. package/ui/TheoremChat.d.ts +2 -2
  59. package/ui/TheoremChat.js +1 -1
  60. package/ui/TheoremDecision.d.ts +1 -1
  61. package/ui/TheoremDecision.js +1 -1
  62. package/ui/TheoremEmbedding.d.ts +1 -1
  63. package/ui/TheoremEmbedding.js +1 -2
  64. package/ui/TheoremHost.d.ts +2 -3
  65. package/ui/TheoremHost.js +1 -3
  66. package/ui/ToolGateCard.d.ts +1 -1
  67. package/ui/ToolGateCard.js +1 -2
  68. package/ui/ToolResult.d.ts +1 -1
  69. package/ui/ToolResult.js +1 -2
  70. package/ui/ToolResultCharts.d.ts +1 -1
  71. package/ui/ToolResultCharts.js +1 -2
  72. package/ui/TraceInspector.d.ts +1 -1
  73. package/ui/TraceInspectorPanel.d.ts +1 -1
  74. package/ui/approval-heading.js +1 -1
  75. package/ui/embedding-space-gl.d.ts +2 -2
  76. package/ui/embedding-space-gl.js +1 -1
  77. package/ui/index.d.ts +1 -1
  78. package/ui/labels.d.ts +1 -2
  79. package/client/audio-level.d.ts +0 -5
  80. package/client/audio-level.js +0 -24
  81. package/client/composer-drawer.d.ts +0 -19
  82. package/client/composer-drawer.js +0 -15
  83. package/client/composer-hints.d.ts +0 -25
  84. package/client/composer-hints.js +0 -20
  85. package/client/composer-primary.d.ts +0 -16
  86. package/client/composer-primary.js +0 -25
  87. package/client/decision-transport.d.ts +0 -70
  88. package/client/decision-transport.js +0 -175
  89. package/client/decode-composer-draft.d.ts +0 -11
  90. package/client/decode-composer-draft.js +0 -12
  91. package/client/embedding-input.d.ts +0 -84
  92. package/client/embedding-input.js +0 -194
  93. package/client/embedding-space.d.ts +0 -82
  94. package/client/embedding-space.js +0 -389
  95. package/client/embedding-transport.d.ts +0 -40
  96. package/client/embedding-transport.js +0 -71
  97. package/client/encode-composer-draft.d.ts +0 -9
  98. package/client/encode-composer-draft.js +0 -34
  99. package/client/encode-files.d.ts +0 -18
  100. package/client/encode-files.js +0 -54
  101. package/client/failure.d.ts +0 -29
  102. package/client/failure.js +0 -34
  103. package/client/generation-selection.d.ts +0 -15
  104. package/client/generation-selection.js +0 -24
  105. package/client/host-transport.d.ts +0 -70
  106. package/client/host-transport.js +0 -75
  107. package/client/live/live-mic-forward.d.ts +0 -30
  108. package/client/live/live-mic-forward.js +0 -46
  109. package/client/live/live-page-tool.d.ts +0 -34
  110. package/client/live/live-page-tool.js +0 -11
  111. package/client/live/live-state.d.ts +0 -13
  112. package/client/live/live-state.js +0 -22
  113. package/client/live/live-tool.d.ts +0 -11
  114. package/client/live/live-tool.js +0 -1
  115. package/client/live-client.d.ts +0 -189
  116. package/client/live-client.js +0 -829
  117. package/client/live-errors.d.ts +0 -2
  118. package/client/live-errors.js +0 -9
  119. package/client/live-messages.d.ts +0 -148
  120. package/client/live-messages.js +0 -100
  121. package/client/mic-capture.d.ts +0 -4
  122. package/client/mic-capture.js +0 -6
  123. package/client/oauth-popup.d.ts +0 -26
  124. package/client/oauth-popup.js +0 -30
  125. package/client/page-dispatch.d.ts +0 -15
  126. package/client/page-dispatch.js +0 -63
  127. package/client/pcm-downsample.d.ts +0 -4
  128. package/client/pcm-downsample.js +0 -45
  129. package/client/run-commit.d.ts +0 -34
  130. package/client/run-commit.js +0 -49
  131. package/client/run-session.d.ts +0 -79
  132. package/client/run-session.js +0 -207
  133. package/client/schema-fields.d.ts +0 -63
  134. package/client/schema-fields.js +0 -199
  135. package/client/shaped-data.d.ts +0 -118
  136. package/client/shaped-data.js +0 -343
  137. package/client/source-citations.d.ts +0 -13
  138. package/client/source-citations.js +0 -56
  139. package/client/space-geometry.d.ts +0 -39
  140. package/client/space-geometry.js +0 -183
  141. package/client/tool-result.d.ts +0 -78
  142. package/client/tool-result.js +0 -293
  143. package/client/tool-resume.d.ts +0 -50
  144. package/client/tool-resume.js +0 -14
  145. package/client/trace-feed.d.ts +0 -13
  146. package/client/trace-feed.js +0 -18
  147. package/client/transcript-schema.d.ts +0 -10
  148. package/client/transcript-schema.js +0 -120
  149. package/client/transport.d.ts +0 -264
  150. package/client/transport.js +0 -330
  151. package/client/turn-client.d.ts +0 -44
  152. package/client/turn-client.js +0 -88
  153. package/client/wire-line.d.ts +0 -56
  154. package/client/wire-line.js +0 -90
  155. package/client/worklets/mic-capture.js +0 -16
  156. package/hooks/use-theorem-chat-actions.d.ts +0 -79
  157. package/hooks/use-theorem-chat-actions.js +0 -287
  158. package/hooks/use-theorem-chat-state.d.ts +0 -66
  159. package/hooks/use-theorem-chat-state.js +0 -122
  160. package/server/checkpoint-ownership.d.ts +0 -6
  161. package/server/checkpoint-ownership.js +0 -42
  162. package/server/conversation-store.d.ts +0 -92
  163. package/server/conversation-store.js +0 -131
  164. package/server/conversation.d.ts +0 -193
  165. package/server/conversation.js +0 -552
  166. package/server/credential-store.d.ts +0 -24
  167. package/server/credential-store.js +0 -14
  168. package/server/decision-handler.d.ts +0 -42
  169. package/server/decision-handler.js +0 -46
  170. package/server/embedding-handler.d.ts +0 -33
  171. package/server/embedding-handler.js +0 -45
  172. package/server/handler.d.ts +0 -158
  173. package/server/handler.js +0 -775
  174. package/server/json-response.d.ts +0 -2
  175. package/server/json-response.js +0 -7
  176. package/server/mcp-handler.d.ts +0 -46
  177. package/server/mcp-handler.js +0 -312
  178. package/server/page-dispatch.d.ts +0 -22
  179. package/server/page-dispatch.js +0 -127
  180. package/server/request-body.d.ts +0 -2
  181. package/server/request-body.js +0 -42
  182. package/server/request-check.d.ts +0 -21
  183. package/server/request-check.js +0 -49
  184. package/server/session-authority.d.ts +0 -8
  185. package/server/session-authority.js +0 -78
  186. package/server/session-boundary.d.ts +0 -26
  187. package/server/session-boundary.js +0 -64
  188. package/server/session-identity.d.ts +0 -16
  189. package/server/session-identity.js +0 -41
  190. package/server/session-limits.d.ts +0 -12
  191. package/server/session-limits.js +0 -59
  192. package/server/session-store.d.ts +0 -123
  193. package/server/session-store.js +0 -264
  194. package/server/single-action.d.ts +0 -21
  195. package/server/single-action.js +0 -30
  196. package/server/steer-inbox.d.ts +0 -35
  197. package/server/steer-inbox.js +0 -52
  198. package/server/tool-operations.d.ts +0 -6
  199. package/server/tool-operations.js +0 -132
  200. package/server/turn-input.d.ts +0 -10
  201. package/server/turn-input.js +0 -29
@@ -1,2 +0,0 @@
1
- /** A JSON reply no cache keeps. */
2
- export declare function jsonResponse(status: number, body: unknown): Response;
@@ -1,7 +0,0 @@
1
- /** A JSON reply no cache keeps. */
2
- export function jsonResponse(status, body) {
3
- return new Response(JSON.stringify(body), {
4
- status,
5
- headers: { 'content-type': 'application/json', 'cache-control': 'no-store' },
6
- });
7
- }
@@ -1,46 +0,0 @@
1
- /**
2
- * One Web-standard handler that serves a host profile's tools to MCP clients over
3
- * Streamable HTTP, at one endpoint that takes POST.
4
- *
5
- * - Clients on the current revision (`2026-07-28`) send each request with its version and
6
- * capabilities in `_meta`; nothing is kept between requests.
7
- * - Clients on an earlier revision open with `initialize`; the handler answers it and serves
8
- * them the same way, without a session.
9
- *
10
- * Every call runs through the kernel as a host call does: the profile's gates, guardrails and
11
- * trace. The client reads what a model would: the call's guarded text, never its raw output.
12
- *
13
- * @module
14
- */
15
- import { type ClientTurnOptions } from '@theoremjs/agents/host';
16
- import { type TheoremHostHandlerOptions } from './handler.ts';
17
- export type TheoremMcpHandlerOptions = {
18
- /** Host profile to serve. Registered with the kernel when the handler is created. */
19
- profile: TheoremHostHandlerOptions['profile'];
20
- /** The server's version, as clients show it. */
21
- version: string;
22
- /** The server's name, as clients show it. Defaults to the profile's id. */
23
- name?: string;
24
- /** How a model should use these tools; clients may add it to the model's context. */
25
- instructions?: string;
26
- /**
27
- * Origins a browser page may call from, besides the endpoint's own. A request whose
28
- * `Origin` is neither is refused with 403, as MCP requires against DNS rebinding.
29
- */
30
- origins?: readonly string[];
31
- /** Handed to each tool as `ctx.host`. */
32
- host?: (request: Request) => unknown;
33
- /** Largest request body read, in bytes. */
34
- maxRequestBytes?: number;
35
- /** Who reads this host's replies: forwarded to `clientError` for a failure the handler catches. */
36
- clientEvents?: ClientTurnOptions;
37
- /** Called with any error the handler catches, for reporting. */
38
- onError?: (err: unknown, ctx: {
39
- request: Request;
40
- }) => void;
41
- };
42
- /**
43
- * Serves a host profile's tools to MCP clients. Every tool the profile allows must run
44
- * without a person's approval: an MCP client has no one to ask.
45
- */
46
- export declare function createTheoremMcpHandler(options: TheoremMcpHandlerOptions): (request: Request) => Promise<Response>;
@@ -1,312 +0,0 @@
1
- /**
2
- * One Web-standard handler that serves a host profile's tools to MCP clients over
3
- * Streamable HTTP, at one endpoint that takes POST.
4
- *
5
- * - Clients on the current revision (`2026-07-28`) send each request with its version and
6
- * capabilities in `_meta`; nothing is kept between requests.
7
- * - Clients on an earlier revision open with `initialize`; the handler answers it and serves
8
- * them the same way, without a session.
9
- *
10
- * Every call runs through the kernel as a host call does: the profile's gates, guardrails and
11
- * trace. The client reads what a model would: the call's guarded text, never its raw output.
12
- *
13
- * @module
14
- */
15
- import { invokeTool, labelText, registerProfile, } from '@theoremjs/agents';
16
- import { caughtStatus, clientError } from '@theoremjs/agents/host';
17
- import { hostInterface } from "../client/host-transport.js";
18
- import { defineHostProfile, hostTool } from "./handler.js";
19
- import { boundedRequestJson, requestByteLimit } from "./request-body.js";
20
- /** The revision that carries its version on every request. */
21
- const MODERN_VERSION = '2026-07-28';
22
- /** Revisions that open with `initialize`, newest first. */
23
- const LEGACY_VERSIONS = ['2025-11-25', '2025-06-18', '2025-03-26'];
24
- const SUPPORTED_VERSIONS = [MODERN_VERSION, ...LEGACY_VERSIONS];
25
- const META_VERSION = 'io.modelcontextprotocol/protocolVersion';
26
- const META_CAPABILITIES = 'io.modelcontextprotocol/clientCapabilities';
27
- const META_SERVER = 'io.modelcontextprotocol/serverInfo';
28
- /**
29
- * How long a current-revision client may keep each cacheable result, in milliseconds. Neither
30
- * holds anything of the caller's, so any cache may share them.
31
- */
32
- const CACHED = {
33
- 'server/discover': 3_600_000,
34
- 'tools/list': 300_000,
35
- };
36
- const PARSE_ERROR = -32700;
37
- const INVALID_REQUEST = -32600;
38
- const METHOD_NOT_FOUND = -32601;
39
- const INVALID_PARAMS = -32602;
40
- const INTERNAL_ERROR = -32603;
41
- const HEADER_MISMATCH = -32020;
42
- const UNSUPPORTED_VERSION = -32022;
43
- class RpcError extends Error {
44
- status;
45
- code;
46
- data;
47
- constructor(status, code, message, data) {
48
- super(message);
49
- this.status = status;
50
- this.code = code;
51
- this.data = data;
52
- }
53
- }
54
- function reply(status, body) {
55
- return new Response(JSON.stringify(body), {
56
- status,
57
- headers: { 'content-type': 'application/json', 'cache-control': 'no-store' },
58
- });
59
- }
60
- function rpcError(id, error) {
61
- return reply(error.status, {
62
- jsonrpc: '2.0',
63
- id,
64
- error: {
65
- code: error.code,
66
- message: error.message,
67
- ...(error.data !== undefined ? { data: error.data } : {}),
68
- },
69
- });
70
- }
71
- function isRecord(value) {
72
- return typeof value === 'object' && value !== null && !Array.isArray(value);
73
- }
74
- /** The JSON-RPC message in `body`; one message per POST, never a batch or a response. */
75
- function rpcRequest(body) {
76
- if (!isRecord(body) ||
77
- body.jsonrpc !== '2.0' ||
78
- typeof body.method !== 'string' ||
79
- (body.id !== undefined && typeof body.id !== 'string' && typeof body.id !== 'number') ||
80
- (body.params !== undefined && !isRecord(body.params)))
81
- // labels-exempt: protocol diagnostic for the MCP client, not end-user copy
82
- throw new RpcError(400, INVALID_REQUEST, 'Invalid request');
83
- return {
84
- ...(body.id !== undefined ? { id: body.id } : {}),
85
- method: body.method,
86
- params: isRecord(body.params) ? body.params : {},
87
- };
88
- }
89
- /** A mirrored header's value, decoded from the `=?base64?…?=` form MCP uses for unsafe values. */
90
- function headerValue(value) {
91
- const encoded = /^=\?base64\?(.*)\?=$/.exec(value);
92
- if (!encoded)
93
- return value;
94
- const bytes = Uint8Array.from(atob(encoded[1]), (char) => char.charCodeAt(0));
95
- return new TextDecoder().decode(bytes);
96
- }
97
- /**
98
- * The current revision's request checks: a version this server speaks, the capabilities the
99
- * client declares, and headers that agree with the body they mirror.
100
- */
101
- function checkModern(request, message, meta) {
102
- const version = meta[META_VERSION];
103
- if (version !== MODERN_VERSION)
104
- // labels-exempt: protocol diagnostic for the MCP client, not end-user copy
105
- throw new RpcError(400, UNSUPPORTED_VERSION, 'Unsupported protocol version', {
106
- supported: SUPPORTED_VERSIONS,
107
- requested: version,
108
- });
109
- if (!isRecord(meta[META_CAPABILITIES]))
110
- // labels-exempt: protocol diagnostic for the MCP client, not end-user copy
111
- throw new RpcError(400, INVALID_PARAMS, `Missing _meta field ${META_CAPABILITIES}`);
112
- const mirrored = [
113
- ['mcp-protocol-version', version],
114
- ['mcp-method', message.method],
115
- ];
116
- if (message.method === 'tools/call')
117
- mirrored.push(['mcp-name', message.params.name]);
118
- for (const [name, body] of mirrored) {
119
- const header = request.headers.get(name);
120
- if (header === null || headerValue(header) !== body)
121
- // labels-exempt: protocol diagnostic for the MCP client, not end-user copy
122
- throw new RpcError(400, HEADER_MISMATCH, `Header mismatch: ${name}`);
123
- }
124
- }
125
- /** The legacy revision a client asked for in `initialize`, or the newest one this server speaks. */
126
- function legacyVersion(params) {
127
- const asked = params.protocolVersion;
128
- return LEGACY_VERSIONS.find((version) => version === asked) ?? LEGACY_VERSIONS[0];
129
- }
130
- /** What the client reads of one call, from the kernel's events for it. */
131
- function callResult(events, labels) {
132
- for (const event of events) {
133
- if (event.type === 'error')
134
- return {
135
- content: [
136
- { type: 'text', text: event.error ?? labelText(`error.${event.errorKind}`, {}, labels) },
137
- ],
138
- isError: true,
139
- };
140
- if (event.type !== 'tool' || !('phase' in event.tool))
141
- continue;
142
- const tool = event.tool;
143
- if (tool.phase === 'complete')
144
- return {
145
- content: [
146
- { type: 'text', text: tool.readBack ?? '' },
147
- ...(tool.parts ?? []).map(partContent),
148
- ],
149
- isError: false,
150
- };
151
- if (tool.phase === 'error')
152
- return {
153
- content: [{ type: 'text', text: tool.readBack ?? tool.failure.message }],
154
- isError: true,
155
- };
156
- // why: No one can answer a gate here; the client reads why the call stopped, as a model would.
157
- if (tool.phase === 'gate')
158
- return {
159
- content: [{ type: 'text', text: tool.readBack ?? labelText('error.blocked', {}, labels) }],
160
- isError: true,
161
- };
162
- }
163
- return {
164
- content: [{ type: 'text', text: labelText('error.internal', {}, labels) }],
165
- isError: true,
166
- };
167
- }
168
- function partContent(part, index) {
169
- if (part.type === 'text')
170
- return { type: 'text', text: part.text };
171
- if ('uri' in part)
172
- return { type: 'resource_link', uri: part.uri, name: part.uri, mimeType: part.mimeType };
173
- if (part.type === 'image' || part.type === 'audio')
174
- return { type: part.type, data: part.data, mimeType: part.mimeType };
175
- return {
176
- type: 'resource',
177
- resource: { uri: `part:${index}`, mimeType: part.mimeType, blob: part.data },
178
- };
179
- }
180
- /** Same origin, or one the host lists; a request with no `Origin` is not from a page. */
181
- function originAllowed(request, origins) {
182
- const origin = request.headers.get('origin');
183
- return origin === null || origin === new URL(request.url).origin || origins.includes(origin);
184
- }
185
- /**
186
- * Serves a host profile's tools to MCP clients. Every tool the profile allows must run
187
- * without a person's approval: an MCP client has no one to ask.
188
- */
189
- export function createTheoremMcpHandler(options) {
190
- const host = defineHostProfile('createTheoremMcpHandler', options.profile);
191
- for (const name of host.tools.allow) {
192
- const permission = hostTool(name)?.permission;
193
- if (permission !== undefined && permission !== 'auto') {
194
- // labels-exempt: builder config error at setup; no user sees it
195
- throw new Error(`createTheoremMcpHandler serves tools that run without approval; '${name}' has permission '${permission}'.`);
196
- }
197
- }
198
- const maxBytes = requestByteLimit(options.maxRequestBytes);
199
- registerProfile(host);
200
- const serverInfo = { name: options.name ?? host.id, version: options.version };
201
- const instructions = options.instructions ? { instructions: options.instructions } : {};
202
- const listed = () => hostInterface(host, hostTool).tools.map((tool) => ({
203
- name: tool.name,
204
- description: tool.description,
205
- inputSchema: tool.inputSchema,
206
- annotations: {
207
- readOnlyHint: tool.access === 'read-only',
208
- ...(tool.access === 'destructive' ? { destructiveHint: true } : {}),
209
- },
210
- }));
211
- async function call(request, params) {
212
- const name = params.name;
213
- if (typeof name !== 'string' || !listed().some((tool) => tool.name === name))
214
- // labels-exempt: protocol diagnostic for the MCP client, not end-user copy
215
- throw new RpcError(200, INVALID_PARAMS, `Unknown tool: ${String(name)}`);
216
- const events = [];
217
- for await (const event of invokeTool({
218
- profile: host.id,
219
- name,
220
- input: params.arguments ?? {},
221
- signal: request.signal,
222
- host: await options.host?.(request),
223
- }))
224
- events.push(event);
225
- return callResult(events, host.labels);
226
- }
227
- async function result(request, message, modern) {
228
- switch (message.method) {
229
- case 'server/discover':
230
- if (!modern)
231
- break;
232
- return {
233
- supportedVersions: SUPPORTED_VERSIONS,
234
- capabilities: { tools: {} },
235
- ...instructions,
236
- };
237
- case 'initialize':
238
- if (modern)
239
- break;
240
- return {
241
- protocolVersion: legacyVersion(message.params),
242
- capabilities: { tools: {} },
243
- serverInfo,
244
- ...instructions,
245
- };
246
- case 'ping':
247
- if (modern)
248
- break;
249
- return {};
250
- case 'tools/list':
251
- return { tools: listed() };
252
- case 'tools/call':
253
- return await call(request, message.params);
254
- }
255
- // labels-exempt: protocol diagnostic for the MCP client, not end-user copy
256
- throw new RpcError(modern ? 404 : 200, METHOD_NOT_FOUND, `Method not found: ${message.method}`);
257
- }
258
- return async (request) => {
259
- if (!originAllowed(request, options.origins ?? []))
260
- // labels-exempt: protocol diagnostic for the MCP client, not end-user copy
261
- return rpcError(null, new RpcError(403, INVALID_REQUEST, 'Origin not allowed'));
262
- if (request.method !== 'POST')
263
- return new Response(null, { status: 405, headers: { allow: 'POST' } });
264
- let id = null;
265
- try {
266
- let body;
267
- try {
268
- body = await boundedRequestJson(request, maxBytes);
269
- }
270
- catch (err) {
271
- if (err instanceof SyntaxError)
272
- // labels-exempt: protocol diagnostic for the MCP client, not end-user copy
273
- throw new RpcError(400, PARSE_ERROR, 'Parse error');
274
- throw err;
275
- }
276
- const message = rpcRequest(body);
277
- id = message.id ?? null;
278
- const meta = isRecord(message.params._meta) ? message.params._meta : {};
279
- const modern = META_VERSION in meta;
280
- if (modern)
281
- checkModern(request, message, meta);
282
- else if (request.headers.get('mcp-protocol-version') === MODERN_VERSION)
283
- // labels-exempt: protocol diagnostic for the MCP client, not end-user copy
284
- throw new RpcError(400, INVALID_PARAMS, `Missing _meta field ${META_VERSION}`);
285
- // invariant: A notification has no reply; the client needs only to know it arrived.
286
- if (message.id === undefined)
287
- return new Response(null, { status: 202 });
288
- const answered = await result(request, message, modern);
289
- const ttlMs = CACHED[message.method];
290
- return reply(200, {
291
- jsonrpc: '2.0',
292
- id: message.id,
293
- result: modern
294
- ? {
295
- resultType: 'complete',
296
- ...answered,
297
- ...(ttlMs !== undefined ? { ttlMs, cacheScope: 'public' } : {}),
298
- _meta: { [META_SERVER]: serverInfo },
299
- }
300
- : answered,
301
- });
302
- }
303
- catch (err) {
304
- if (err instanceof RpcError)
305
- return rpcError(id, err);
306
- options.onError?.(err, { request });
307
- const failure = clientError(err, host.labels, options.clientEvents);
308
- const code = failure.errorKind === 'request' ? INVALID_REQUEST : INTERNAL_ERROR;
309
- return rpcError(id, new RpcError(caughtStatus(err), code, failure.error));
310
- }
311
- };
312
- }
@@ -1,22 +0,0 @@
1
- import { type PageAnswer, type PageToolExecution, z } from '@theoremjs/agents';
2
- import type { MutableStore } from './session-authority.ts';
3
- import type { TheoremSessionState } from './session-store.ts';
4
- export type PageToolResult = {
5
- callId: string;
6
- operationId: string;
7
- grantId: string;
8
- page: PageAnswer;
9
- };
10
- export declare const pageToolResultSchema: z.ZodType<PageToolResult>;
11
- export interface PageToolBroker {
12
- reserve(sessionId: string, grantId: string, signal?: AbortSignal): {
13
- result: Promise<PageAnswer>;
14
- cancel(): void;
15
- };
16
- deliver(sessionId: string, grantId: string, page: PageAnswer): Promise<void>;
17
- }
18
- export declare function createMemoryPageToolBroker(maxPending?: number): PageToolBroker;
19
- export declare function sessionPageDispatch(store: MutableStore<TheoremSessionState>, sessionId: string, broker: PageToolBroker, timeoutMs?: number): {
20
- execution: PageToolExecution;
21
- answer(result: PageToolResult): Promise<boolean>;
22
- };
@@ -1,127 +0,0 @@
1
- import { jsonValueSchema, labelText, TheoremError, toolInvocationHash, z, } from '@theoremjs/agents';
2
- export const pageToolResultSchema = z.strictObject({
3
- callId: z.string().min(1),
4
- operationId: z.string().min(1).max(256),
5
- grantId: z.string().min(1),
6
- page: z.union([
7
- z.strictObject({ output: jsonValueSchema }),
8
- z.strictObject({ unanswered: z.literal(true) }),
9
- z.strictObject({ timedOut: z.literal(true) }),
10
- ]),
11
- });
12
- function unavailable() {
13
- throw new TheoremError('unavailable', labelText('tool.execution_uncertain'), {
14
- copy: { key: 'tool.execution_uncertain' },
15
- });
16
- }
17
- export function createMemoryPageToolBroker(maxPending = 128) {
18
- if (!Number.isSafeInteger(maxPending) || maxPending <= 0)
19
- throw new TheoremError('config', labelText('error.config'));
20
- const waiting = new Map();
21
- const key = (sessionId, grantId) => JSON.stringify([sessionId, grantId]);
22
- return {
23
- reserve(sessionId, grantId, signal) {
24
- const id = key(sessionId, grantId);
25
- if (signal?.aborted || waiting.has(id) || waiting.size >= maxPending)
26
- unavailable();
27
- let finish = () => { };
28
- const result = new Promise((resolve) => {
29
- finish = resolve;
30
- });
31
- const cancel = () => {
32
- waiting.delete(id);
33
- signal?.removeEventListener('abort', cancel);
34
- finish({ timedOut: true });
35
- };
36
- waiting.set(id, (answer) => {
37
- waiting.delete(id);
38
- signal?.removeEventListener('abort', cancel);
39
- finish(answer);
40
- });
41
- signal?.addEventListener('abort', cancel, { once: true });
42
- return { result, cancel };
43
- },
44
- deliver(sessionId, grantId, page) {
45
- const finish = waiting.get(key(sessionId, grantId));
46
- if (!finish)
47
- return Promise.reject(new TheoremError('unavailable', labelText('tool.execution_uncertain')));
48
- finish(page);
49
- return Promise.resolve();
50
- },
51
- };
52
- }
53
- export function sessionPageDispatch(store, sessionId, broker, timeoutMs = 20_000) {
54
- if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2147483647)
55
- throw new TheoremError('config', labelText('error.config'));
56
- return {
57
- execution: {
58
- async dispatch(claim, signal) {
59
- if (!claim.invocation)
60
- unavailable();
61
- const grantId = crypto.randomUUID();
62
- const expiresAt = Date.now() + timeoutMs;
63
- const pending = broker.reserve(sessionId, grantId, signal);
64
- try {
65
- await store.mutate(sessionId, (state) => {
66
- const record = state.operations[claim.operationId];
67
- if (record?.status !== 'running' ||
68
- record.claimToken !== claim.claimToken ||
69
- record.invocationHash !== claim.invocationHash ||
70
- record.pageGrant)
71
- unavailable();
72
- record.pageGrant = { grantId, expiresAt };
73
- });
74
- }
75
- catch (error) {
76
- pending.cancel();
77
- throw error;
78
- }
79
- const remaining = expiresAt - Date.now();
80
- if (remaining <= 0 || signal?.aborted) {
81
- pending.cancel();
82
- unavailable();
83
- }
84
- let expire = () => { };
85
- const expired = new Promise((resolve) => {
86
- expire = () => resolve({ timedOut: true });
87
- });
88
- const timer = setTimeout(expire, remaining);
89
- signal?.addEventListener('abort', expire, { once: true });
90
- return {
91
- grantId,
92
- result: Promise.race([pending.result, expired]).finally(() => {
93
- clearTimeout(timer);
94
- signal?.removeEventListener('abort', expire);
95
- pending.cancel();
96
- }),
97
- };
98
- },
99
- },
100
- async answer(raw) {
101
- const result = pageToolResultSchema.parse(raw);
102
- const hash = await toolInvocationHash(result.page);
103
- const duplicate = await store.mutate(sessionId, (state) => {
104
- const record = state.operations[result.operationId];
105
- if (!record ||
106
- record.status === 'ready' ||
107
- record.callId !== result.callId ||
108
- record.pageGrant?.grantId !== result.grantId)
109
- unavailable();
110
- if (record.pageGrant.resultHash !== undefined) {
111
- if (record.pageGrant.resultHash !== hash)
112
- unavailable();
113
- if (record.status === 'uncertain')
114
- unavailable();
115
- return { duplicate: true, settled: record.status === 'settled' };
116
- }
117
- if (record.status !== 'running' || Date.now() >= record.pageGrant.expiresAt)
118
- unavailable();
119
- record.pageGrant.resultHash = hash;
120
- return { duplicate: false, settled: false };
121
- });
122
- if (!duplicate.duplicate)
123
- await broker.deliver(sessionId, result.grantId, result.page);
124
- return duplicate.settled;
125
- },
126
- };
127
- }
@@ -1,2 +0,0 @@
1
- export declare function requestByteLimit(bytes?: number): number;
2
- export declare function boundedRequestJson(request: Request, maxBytes: number): Promise<unknown>;
@@ -1,42 +0,0 @@
1
- import { labelText, TheoremError, throwIfAborted } from '@theoremjs/agents';
2
- const DEFAULT_REQUEST_BYTES = 16 * 1024 * 1024;
3
- export function requestByteLimit(bytes = DEFAULT_REQUEST_BYTES) {
4
- if (!Number.isSafeInteger(bytes) || bytes <= 0) {
5
- throw new TheoremError('config', labelText('error.config'));
6
- }
7
- return bytes;
8
- }
9
- export async function boundedRequestJson(request, maxBytes) {
10
- if (Number(request.headers.get('content-length')) > maxBytes || !request.body) {
11
- throw new TheoremError('request', labelText('error.request'));
12
- }
13
- const reader = request.body.getReader();
14
- const abort = () => {
15
- void reader.cancel(request.signal.reason).catch(() => undefined);
16
- };
17
- request.signal.addEventListener('abort', abort, { once: true });
18
- const decoder = new TextDecoder();
19
- const text = [];
20
- let bytes = 0;
21
- try {
22
- for (;;) {
23
- throwIfAborted(request.signal);
24
- const next = await reader.read();
25
- throwIfAborted(request.signal);
26
- if (next.done)
27
- break;
28
- bytes += next.value.byteLength;
29
- if (bytes > maxBytes)
30
- throw new TheoremError('request', labelText('error.request'));
31
- if (next.value.byteLength)
32
- text.push(decoder.decode(next.value, { stream: true }));
33
- }
34
- text.push(decoder.decode());
35
- return JSON.parse(text.join(''));
36
- }
37
- finally {
38
- request.signal.removeEventListener('abort', abort);
39
- void reader.cancel().catch(() => undefined);
40
- reader.releaseLock();
41
- }
42
- }
@@ -1,21 +0,0 @@
1
- /**
2
- * What a browser sends a host, checked: the handler's request bodies and a
3
- * relay's live messages. A malformed one is a `request` error, which names
4
- * only the paths and codes that broke, never the value.
5
- *
6
- * @module
7
- */
8
- import { type z } from '@theoremjs/agents';
9
- import { type LiveClientMessage, type LiveOpenMessage } from '../client/live-messages.ts';
10
- /** `raw` checked against `schema`; a missing or malformed field is a `request` error. */
11
- export declare function checkRequest<T>(schema: z.ZodType<T>, raw: unknown, what: string): T;
12
- /**
13
- * One text frame from the live client, as a relay reads it: JSON that passes
14
- * `liveClientMessageSchema`, else a `request` error.
15
- */
16
- export declare function parseLiveClientMessage(text: string): LiveClientMessage;
17
- /**
18
- * The live client's first frame, as a relay reads it before it opens the
19
- * session: JSON that passes `liveOpenMessageSchema`, else a `request` error.
20
- */
21
- export declare function parseLiveOpenMessage(text: string): LiveOpenMessage;
@@ -1,49 +0,0 @@
1
- /**
2
- * What a browser sends a host, checked: the handler's request bodies and a
3
- * relay's live messages. A malformed one is a `request` error, which names
4
- * only the paths and codes that broke, never the value.
5
- *
6
- * @module
7
- */
8
- import { labelText, TheoremError } from '@theoremjs/agents';
9
- import { liveClientMessageSchema, liveOpenMessageSchema, } from "../client/live-messages.js";
10
- import { issueSummary } from "../client/wire-line.js";
11
- import { requestByteLimit } from "./request-body.js";
12
- /** `raw` checked against `schema`; a missing or malformed field is a `request` error. */
13
- export function checkRequest(schema, raw, what) {
14
- const parsed = schema.safeParse(raw);
15
- if (!parsed.success) {
16
- // labels-exempt: internal diagnostic; the user reads the error kind's (or copy key's) wording
17
- throw new TheoremError('request', `${what} failed its check: ${issueSummary(parsed.error)}`);
18
- }
19
- return parsed.data;
20
- }
21
- function liveFrame(text) {
22
- const limit = requestByteLimit();
23
- if (text.length > limit || new TextEncoder().encode(text).byteLength > limit)
24
- throw new TheoremError('request', labelText('error.request'));
25
- try {
26
- return JSON.parse(text);
27
- }
28
- catch {
29
- // invariant: JSON parser diagnostics can contain browser values.
30
- throw new TheoremError('request', labelText('error.request'), {
31
- cause: { code: 'request_json' },
32
- });
33
- }
34
- }
35
- /**
36
- * One text frame from the live client, as a relay reads it: JSON that passes
37
- * `liveClientMessageSchema`, else a `request` error.
38
- */
39
- export function parseLiveClientMessage(text) {
40
- return checkRequest(liveClientMessageSchema, liveFrame(text), 'live message');
41
- }
42
- /**
43
- * The live client's first frame, as a relay reads it before it opens the
44
- * session: JSON that passes `liveOpenMessageSchema`, else a `request` error.
45
- */
46
- export function parseLiveOpenMessage(text) {
47
- // labels-exempt: internal diagnostic; the user reads the error kind's wording
48
- return checkRequest(liveOpenMessageSchema, liveFrame(text), 'live open message');
49
- }
@@ -1,8 +0,0 @@
1
- import { type TheoremSessionLimits } from './session-limits.ts';
2
- import { type TheoremSessionState, type TheoremSessionStore } from './session-store.ts';
3
- export type MutableStore<T> = {
4
- read(sessionId: string): Promise<T>;
5
- mutate<R>(sessionId: string, change: (value: T) => R | Promise<R>): Promise<R>;
6
- };
7
- /** Session state, with expired gates dropped as it loads so they can't be answered. */
8
- export declare function sessionStateStore(store: TheoremSessionStore, gateTtlMs: number, operationRetentionMs: number, limits: TheoremSessionLimits): MutableStore<TheoremSessionState>;