@theoremjs/react 0.0.0-stage → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (320) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +772 -2
  3. package/client/attachment-hover-preview.d.ts +11 -0
  4. package/client/attachment-hover-preview.js +36 -0
  5. package/client/audio-level.d.ts +5 -0
  6. package/client/audio-level.js +24 -0
  7. package/client/composer-attachments.d.ts +30 -0
  8. package/client/composer-attachments.js +33 -0
  9. package/client/composer-drawer.d.ts +19 -0
  10. package/client/composer-drawer.js +15 -0
  11. package/client/composer-hints.d.ts +25 -0
  12. package/client/composer-hints.js +20 -0
  13. package/client/composer-primary.d.ts +16 -0
  14. package/client/composer-primary.js +25 -0
  15. package/client/decision-images.d.ts +76 -0
  16. package/client/decision-images.js +139 -0
  17. package/client/decision-transport.d.ts +70 -0
  18. package/client/decision-transport.js +175 -0
  19. package/client/decode-composer-draft.d.ts +11 -0
  20. package/client/decode-composer-draft.js +12 -0
  21. package/client/embedding-input.d.ts +84 -0
  22. package/client/embedding-input.js +194 -0
  23. package/client/embedding-space.d.ts +82 -0
  24. package/client/embedding-space.js +389 -0
  25. package/client/embedding-transport.d.ts +40 -0
  26. package/client/embedding-transport.js +71 -0
  27. package/client/encode-composer-draft.d.ts +9 -0
  28. package/client/encode-composer-draft.js +34 -0
  29. package/client/encode-files.d.ts +18 -0
  30. package/client/encode-files.js +54 -0
  31. package/client/failure.d.ts +29 -0
  32. package/client/failure.js +34 -0
  33. package/client/generation-selection.d.ts +15 -0
  34. package/client/generation-selection.js +24 -0
  35. package/client/host-call.d.ts +51 -0
  36. package/client/host-call.js +47 -0
  37. package/client/host-transport.d.ts +70 -0
  38. package/client/host-transport.js +75 -0
  39. package/client/image-output.d.ts +6 -0
  40. package/client/image-output.js +13 -0
  41. package/client/index.d.ts +28 -0
  42. package/client/index.js +22 -0
  43. package/client/ink-waveform.d.ts +17 -0
  44. package/client/ink-waveform.js +68 -0
  45. package/client/live/apply-live-turn-tool-event.d.ts +14 -0
  46. package/client/live/apply-live-turn-tool-event.js +39 -0
  47. package/client/live/live-captions.d.ts +53 -0
  48. package/client/live/live-captions.js +174 -0
  49. package/client/live/live-mic-forward.d.ts +30 -0
  50. package/client/live/live-mic-forward.js +46 -0
  51. package/client/live/live-page-tool.d.ts +34 -0
  52. package/client/live/live-page-tool.js +11 -0
  53. package/client/live/live-state.d.ts +13 -0
  54. package/client/live/live-state.js +22 -0
  55. package/client/live/live-tool.d.ts +11 -0
  56. package/client/live/live-tool.js +1 -0
  57. package/client/live/live-video.d.ts +10 -0
  58. package/client/live/live-video.js +71 -0
  59. package/client/live/run-live-tool-call.d.ts +22 -0
  60. package/client/live/run-live-tool-call.js +70 -0
  61. package/client/live/use-live-caption-log.d.ts +17 -0
  62. package/client/live/use-live-caption-log.js +51 -0
  63. package/client/live-client.d.ts +189 -0
  64. package/client/live-client.js +829 -0
  65. package/client/live-errors.d.ts +2 -0
  66. package/client/live-errors.js +9 -0
  67. package/client/live-messages.d.ts +148 -0
  68. package/client/live-messages.js +100 -0
  69. package/client/mic-capture.d.ts +4 -0
  70. package/client/mic-capture.js +6 -0
  71. package/client/oauth-popup.d.ts +26 -0
  72. package/client/oauth-popup.js +30 -0
  73. package/client/page-dispatch.d.ts +15 -0
  74. package/client/page-dispatch.js +63 -0
  75. package/client/pcm-downsample.d.ts +4 -0
  76. package/client/pcm-downsample.js +45 -0
  77. package/client/run-commit.d.ts +34 -0
  78. package/client/run-commit.js +49 -0
  79. package/client/run-session.d.ts +79 -0
  80. package/client/run-session.js +207 -0
  81. package/client/schema-fields.d.ts +63 -0
  82. package/client/schema-fields.js +199 -0
  83. package/client/shaped-data.d.ts +118 -0
  84. package/client/shaped-data.js +343 -0
  85. package/client/source-citations.d.ts +13 -0
  86. package/client/source-citations.js +56 -0
  87. package/client/space-geometry.d.ts +39 -0
  88. package/client/space-geometry.js +183 -0
  89. package/client/state-fields.d.ts +16 -0
  90. package/client/state-fields.js +44 -0
  91. package/client/tool-result.d.ts +78 -0
  92. package/client/tool-result.js +293 -0
  93. package/client/tool-resume.d.ts +50 -0
  94. package/client/tool-resume.js +14 -0
  95. package/client/trace-feed.d.ts +13 -0
  96. package/client/trace-feed.js +18 -0
  97. package/client/trace-story.d.ts +163 -0
  98. package/client/trace-story.js +440 -0
  99. package/client/trace-view.d.ts +130 -0
  100. package/client/trace-view.js +333 -0
  101. package/client/transcript-groups.d.ts +140 -0
  102. package/client/transcript-groups.js +217 -0
  103. package/client/transcript-schema.d.ts +10 -0
  104. package/client/transcript-schema.js +120 -0
  105. package/client/transcript-scroll.d.ts +7 -0
  106. package/client/transcript-scroll.js +6 -0
  107. package/client/transport.d.ts +264 -0
  108. package/client/transport.js +330 -0
  109. package/client/turn-client.d.ts +44 -0
  110. package/client/turn-client.js +88 -0
  111. package/client/voice-label.d.ts +2 -0
  112. package/client/voice-label.js +15 -0
  113. package/client/voice-recorder.d.ts +27 -0
  114. package/client/voice-recorder.js +162 -0
  115. package/client/wire-line.d.ts +56 -0
  116. package/client/wire-line.js +90 -0
  117. package/client/worklets/mic-capture.js +16 -0
  118. package/components/InkWaveform.d.ts +21 -0
  119. package/components/InkWaveform.js +131 -0
  120. package/components/live/use-live-runner-controls.d.ts +47 -0
  121. package/components/live/use-live-runner-controls.js +96 -0
  122. package/components/live/use-live-runner-model.d.ts +49 -0
  123. package/components/live/use-live-runner-model.js +190 -0
  124. package/components/live/use-live-runner-ui.d.ts +67 -0
  125. package/components/live/use-live-runner-ui.js +167 -0
  126. package/components/live/use-live-session-client.d.ts +41 -0
  127. package/components/live/use-live-session-client.js +126 -0
  128. package/components/use-composer-voice.d.ts +28 -0
  129. package/components/use-composer-voice.js +90 -0
  130. package/components/use-editor-selection.d.ts +3 -0
  131. package/components/use-editor-selection.js +19 -0
  132. package/components/use-voice-playback.d.ts +17 -0
  133. package/components/use-voice-playback.js +68 -0
  134. package/hooks/use-described.d.ts +12 -0
  135. package/hooks/use-described.js +25 -0
  136. package/hooks/use-embedding-index.d.ts +27 -0
  137. package/hooks/use-embedding-index.js +43 -0
  138. package/hooks/use-one-call.d.ts +31 -0
  139. package/hooks/use-one-call.js +76 -0
  140. package/hooks/use-page-tool-warnings.d.ts +6 -0
  141. package/hooks/use-page-tool-warnings.js +25 -0
  142. package/hooks/use-second-ticker.d.ts +2 -0
  143. package/hooks/use-second-ticker.js +13 -0
  144. package/hooks/use-theorem-chat-actions.d.ts +79 -0
  145. package/hooks/use-theorem-chat-actions.js +287 -0
  146. package/hooks/use-theorem-chat-state.d.ts +66 -0
  147. package/hooks/use-theorem-chat-state.js +122 -0
  148. package/hooks/use-theorem-chat.d.ts +97 -0
  149. package/hooks/use-theorem-chat.js +528 -0
  150. package/hooks/use-theorem-decision.d.ts +18 -0
  151. package/hooks/use-theorem-decision.js +12 -0
  152. package/hooks/use-theorem-embedding.d.ts +18 -0
  153. package/hooks/use-theorem-embedding.js +12 -0
  154. package/hooks/use-theorem-host.d.ts +45 -0
  155. package/hooks/use-theorem-host.js +135 -0
  156. package/hooks/use-theorem-interface.d.ts +22 -0
  157. package/hooks/use-theorem-interface.js +15 -0
  158. package/hooks/use-trace-records.d.ts +4 -0
  159. package/hooks/use-trace-records.js +7 -0
  160. package/index.d.ts +17 -0
  161. package/index.js +17 -0
  162. package/live.d.ts +9 -0
  163. package/live.js +10 -0
  164. package/package.json +74 -4
  165. package/server/checkpoint-ownership.d.ts +6 -0
  166. package/server/checkpoint-ownership.js +42 -0
  167. package/server/conversation-store.d.ts +92 -0
  168. package/server/conversation-store.js +131 -0
  169. package/server/conversation.d.ts +193 -0
  170. package/server/conversation.js +552 -0
  171. package/server/credential-store.d.ts +24 -0
  172. package/server/credential-store.js +14 -0
  173. package/server/decision-handler.d.ts +42 -0
  174. package/server/decision-handler.js +46 -0
  175. package/server/embedding-handler.d.ts +33 -0
  176. package/server/embedding-handler.js +45 -0
  177. package/server/handler.d.ts +152 -0
  178. package/server/handler.js +771 -0
  179. package/server/json-response.d.ts +2 -0
  180. package/server/json-response.js +7 -0
  181. package/server/mod.d.ts +25 -0
  182. package/server/mod.js +23 -0
  183. package/server/page-dispatch.d.ts +22 -0
  184. package/server/page-dispatch.js +127 -0
  185. package/server/request-body.d.ts +2 -0
  186. package/server/request-body.js +42 -0
  187. package/server/request-check.d.ts +21 -0
  188. package/server/request-check.js +49 -0
  189. package/server/session-authority.d.ts +8 -0
  190. package/server/session-authority.js +78 -0
  191. package/server/session-boundary.d.ts +26 -0
  192. package/server/session-boundary.js +64 -0
  193. package/server/session-identity.d.ts +16 -0
  194. package/server/session-identity.js +41 -0
  195. package/server/session-limits.d.ts +12 -0
  196. package/server/session-limits.js +59 -0
  197. package/server/session-store.d.ts +123 -0
  198. package/server/session-store.js +264 -0
  199. package/server/single-action.d.ts +21 -0
  200. package/server/single-action.js +30 -0
  201. package/server/steer-inbox.d.ts +35 -0
  202. package/server/steer-inbox.js +52 -0
  203. package/server/tool-operations.d.ts +6 -0
  204. package/server/tool-operations.js +132 -0
  205. package/server/turn-input.d.ts +10 -0
  206. package/server/turn-input.js +29 -0
  207. package/styles/ink-controls.css +134 -0
  208. package/ui/CardWall.d.ts +10 -0
  209. package/ui/CardWall.js +61 -0
  210. package/ui/ChatComposerBar.d.ts +62 -0
  211. package/ui/ChatComposerBar.js +306 -0
  212. package/ui/ChatTranscript.d.ts +43 -0
  213. package/ui/ChatTranscript.js +527 -0
  214. package/ui/ConsoleFrame.d.ts +104 -0
  215. package/ui/ConsoleFrame.js +146 -0
  216. package/ui/DecisionAnswers.d.ts +53 -0
  217. package/ui/DecisionAnswers.js +233 -0
  218. package/ui/DecisionImages.d.ts +16 -0
  219. package/ui/DecisionImages.js +46 -0
  220. package/ui/EmbeddingDocuments.d.ts +78 -0
  221. package/ui/EmbeddingDocuments.js +278 -0
  222. package/ui/EmbeddingQuery.d.ts +27 -0
  223. package/ui/EmbeddingQuery.js +95 -0
  224. package/ui/EmbeddingResults.d.ts +41 -0
  225. package/ui/EmbeddingResults.js +165 -0
  226. package/ui/EmbeddingSpace.d.ts +26 -0
  227. package/ui/EmbeddingSpace.js +106 -0
  228. package/ui/LiveCaptionsPanel.d.ts +22 -0
  229. package/ui/LiveCaptionsPanel.js +27 -0
  230. package/ui/LiveRunner.d.ts +30 -0
  231. package/ui/LiveRunner.js +180 -0
  232. package/ui/PaneState.d.ts +33 -0
  233. package/ui/PaneState.js +50 -0
  234. package/ui/SchemaFields.d.ts +10 -0
  235. package/ui/SchemaFields.js +150 -0
  236. package/ui/ShapedData.d.ts +11 -0
  237. package/ui/ShapedData.js +196 -0
  238. package/ui/SidePanel.d.ts +126 -0
  239. package/ui/SidePanel.js +123 -0
  240. package/ui/StateFields.d.ts +44 -0
  241. package/ui/StateFields.js +139 -0
  242. package/ui/TheoremChat.d.ts +122 -0
  243. package/ui/TheoremChat.js +134 -0
  244. package/ui/TheoremDecision.d.ts +30 -0
  245. package/ui/TheoremDecision.js +130 -0
  246. package/ui/TheoremEmbedding.d.ts +27 -0
  247. package/ui/TheoremEmbedding.js +122 -0
  248. package/ui/TheoremHost.d.ts +55 -0
  249. package/ui/TheoremHost.js +240 -0
  250. package/ui/ToolGateCard.d.ts +32 -0
  251. package/ui/ToolGateCard.js +107 -0
  252. package/ui/ToolResult.d.ts +10 -0
  253. package/ui/ToolResult.js +62 -0
  254. package/ui/ToolResultCharts.d.ts +5 -0
  255. package/ui/ToolResultCharts.js +103 -0
  256. package/ui/TraceCharts.d.ts +13 -0
  257. package/ui/TraceCharts.js +266 -0
  258. package/ui/TraceGuardrails.d.ts +11 -0
  259. package/ui/TraceGuardrails.js +74 -0
  260. package/ui/TraceInspector.d.ts +16 -0
  261. package/ui/TraceInspector.js +206 -0
  262. package/ui/TraceInspectorPanel.d.ts +46 -0
  263. package/ui/TraceInspectorPanel.js +95 -0
  264. package/ui/TraceOverview.d.ts +38 -0
  265. package/ui/TraceOverview.js +126 -0
  266. package/ui/TraceSpanDetail.d.ts +10 -0
  267. package/ui/TraceSpanDetail.js +190 -0
  268. package/ui/TraceStory.d.ts +20 -0
  269. package/ui/TraceStory.js +108 -0
  270. package/ui/TraceValues.d.ts +36 -0
  271. package/ui/TraceValues.js +123 -0
  272. package/ui/TraceWaterfall.d.ts +13 -0
  273. package/ui/TraceWaterfall.js +135 -0
  274. package/ui/VoiceNote.d.ts +12 -0
  275. package/ui/VoiceNote.js +28 -0
  276. package/ui/approval-heading.d.ts +4 -0
  277. package/ui/approval-heading.js +9 -0
  278. package/ui/arrive.d.ts +18 -0
  279. package/ui/arrive.js +46 -0
  280. package/ui/built/theme.css +792 -0
  281. package/ui/built/theme.css.d.ts +9 -0
  282. package/ui/built/theorem.d.ts +13 -0
  283. package/ui/built/theorem.js +660 -0
  284. package/ui/built/theorem.variants.d.ts +22 -0
  285. package/ui/code-language.d.ts +12 -0
  286. package/ui/code-language.js +50 -0
  287. package/ui/disclosure-motion.d.ts +6 -0
  288. package/ui/disclosure-motion.js +207 -0
  289. package/ui/embedding-space-gl.d.ts +37 -0
  290. package/ui/embedding-space-gl.js +663 -0
  291. package/ui/embedding-space-view.d.ts +8 -0
  292. package/ui/embedding-space-view.js +8 -0
  293. package/ui/glide.d.ts +7 -0
  294. package/ui/glide.js +44 -0
  295. package/ui/icons.d.ts +4 -0
  296. package/ui/icons.js +38 -0
  297. package/ui/index.d.ts +36 -0
  298. package/ui/index.js +39 -0
  299. package/ui/labels-provider.d.ts +16 -0
  300. package/ui/labels-provider.js +64 -0
  301. package/ui/labels.d.ts +1442 -0
  302. package/ui/labels.js +1443 -0
  303. package/ui/markdown-plugins.d.ts +9 -0
  304. package/ui/markdown-plugins.js +13 -0
  305. package/ui/mcp-icon.d.ts +6 -0
  306. package/ui/mcp-icon.js +8 -0
  307. package/ui/row-keys.d.ts +19 -0
  308. package/ui/row-keys.js +33 -0
  309. package/ui/theme.d.ts +10 -0
  310. package/ui/theme.js +13 -0
  311. package/ui/theorem-theme.d.ts +10 -0
  312. package/ui/theorem-theme.js +105 -0
  313. package/ui/trace-palette.d.ts +10 -0
  314. package/ui/trace-palette.js +10 -0
  315. package/ui/trace-patterns.d.ts +40 -0
  316. package/ui/trace-patterns.js +57 -0
  317. package/ui/transcript-copy-text.d.ts +4 -0
  318. package/ui/transcript-copy-text.js +40 -0
  319. package/ui/wall-columns.d.ts +8 -0
  320. package/ui/wall-columns.js +19 -0
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Server-held session state for `createTheoremHandler`.
3
+ *
4
+ * Everything that grants authority lives here, never in the request body:
5
+ * which tools the user allowed for the session, which paused tool calls may be
6
+ * resumed (and with exactly what input), and which provider interactions this
7
+ * session may continue.
8
+ *
9
+ * @module
10
+ */
11
+ import { z } from '@theoremjs/agents';
12
+ import type { ToolGateAuth } from '@theoremjs/agents/interface';
13
+ import type { AnsweredGate, ToolGate, ToolId, ToolOperationIdentity, ToolOperationOutcome, ToolOperationUncertainty, TurnInput, TurnToolSnapshot } from '@theoremjs/agents/kernel';
14
+ import { type TheoremSessionLimits } from './session-limits.ts';
15
+ /** A tool call the kernel paused on a gate, as the server saw it. */
16
+ export type PendingToolGate = {
17
+ profileId: string;
18
+ operationId: string;
19
+ callId: string;
20
+ name: string;
21
+ /** The model's arguments; an approval runs the call with exactly these. */
22
+ arguments: Record<string, unknown>;
23
+ /** The gate, and for a sign-in gate the credential slot and kind it waits for. */
24
+ gate: Pick<ToolGate, 'kind' | 'permission'> & {
25
+ auth?: ToolGateAuth;
26
+ };
27
+ snapshot?: TurnToolSnapshot;
28
+ promoted: ToolId[];
29
+ turnInput: TurnInput;
30
+ model?: string;
31
+ createdAt: number;
32
+ expiresAt?: number;
33
+ };
34
+ export type SessionPageGrant = {
35
+ grantId: string;
36
+ expiresAt: number;
37
+ resultHash?: string;
38
+ };
39
+ export type SessionToolOperation = {
40
+ status: 'ready';
41
+ profileId: string;
42
+ pending: PendingToolGate;
43
+ answer: Omit<AnsweredGate, 'typed'>;
44
+ readyAt: number;
45
+ } | (ToolOperationIdentity & {
46
+ status: 'running';
47
+ claimToken: string;
48
+ startedAt: number;
49
+ pageGrant?: SessionPageGrant;
50
+ }) | (ToolOperationIdentity & {
51
+ status: 'settled';
52
+ pageGrant?: SessionPageGrant;
53
+ outcome: ToolOperationOutcome;
54
+ settledAt: number;
55
+ retainUntil?: number;
56
+ }) | (ToolOperationIdentity & {
57
+ status: 'uncertain';
58
+ pageGrant?: SessionPageGrant;
59
+ claimToken: string;
60
+ reason: ToolOperationUncertainty;
61
+ detectedAt: number;
62
+ });
63
+ export declare const DEFAULT_OPERATION_RETENTION_MS: number;
64
+ export type TheoremSessionState = {
65
+ version: 3;
66
+ operations: Record<string, SessionToolOperation>;
67
+ /** Tool ids the user allowed for the rest of this session. */
68
+ permissions: string[];
69
+ /** invariant: Operation IDs index issued gates; both IDs must match an answer. */
70
+ gates: Record<string, PendingToolGate>;
71
+ /** Checkpoint identities this session produced, newest last. */
72
+ providerCheckpoints: string[];
73
+ };
74
+ /** Stored authority is checked before the handler reads or changes it. */
75
+ export declare const theoremSessionStateSchema: z.ZodType<TheoremSessionState>;
76
+ export interface SessionSnapshot {
77
+ revision: string;
78
+ state: TheoremSessionState;
79
+ }
80
+ export type SessionWriteResult = {
81
+ written: true;
82
+ revision: string;
83
+ } | {
84
+ written: false;
85
+ };
86
+ /**
87
+ * Where session state lives. The default is process memory — use a shared
88
+ * store (KV, Redis, a database row) when requests can reach different instances.
89
+ */
90
+ export interface TheoremSessionStore {
91
+ load(sessionId: string): Promise<SessionSnapshot | undefined>;
92
+ /** Compare and replace atomically; null creates only an absent session. */
93
+ compareAndSwap(sessionId: string, expectedRevision: string | null, next: TheoremSessionState): Promise<SessionWriteResult>;
94
+ }
95
+ export declare function emptySessionState(): TheoremSessionState;
96
+ /** Drop gates (waiting or settled) older than `ttlMs`. */
97
+ export declare function pruneGates<Gate extends {
98
+ createdAt: number;
99
+ expiresAt?: number;
100
+ }>(gates: Record<string, Gate>, now: number, ttlMs: number): Record<string, Gate>;
101
+ export type MemorySessionStoreOptions = {
102
+ /** Finite authority storage bounds. */
103
+ sessionLimits?: Partial<TheoremSessionLimits>;
104
+ /** invariant: Idle retention is in milliseconds. Protected authority remains. Default 24h. */
105
+ ttlMs?: number;
106
+ /** Oldest sessions are evicted past this count. Default 10 000. */
107
+ maxSessions?: number;
108
+ };
109
+ export declare function memoryStoreLimits(options: MemorySessionStoreOptions): {
110
+ ttlMs: number;
111
+ maxSessions: number;
112
+ };
113
+ /** A value kept per session id for the separate credential store. */
114
+ export type PerSessionStore<T> = {
115
+ load(sessionId: string): T | undefined | Promise<T | undefined>;
116
+ save(sessionId: string, value: T): void | Promise<void>;
117
+ };
118
+ /** A per-session value kept in process memory: forgotten when idle, oldest evicted first. */
119
+ export declare function createMemorySessionMap<T>(options?: MemorySessionStoreOptions): {
120
+ load(sessionId: string): T | undefined;
121
+ save(sessionId: string, value: T): void;
122
+ };
123
+ export declare function createMemorySessionStore(options?: MemorySessionStoreOptions): TheoremSessionStore;
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Server-held session state for `createTheoremHandler`.
3
+ *
4
+ * Everything that grants authority lives here, never in the request body:
5
+ * which tools the user allowed for the session, which paused tool calls may be
6
+ * resumed (and with exactly what input), and which provider interactions this
7
+ * session may continue.
8
+ *
9
+ * @module
10
+ */
11
+ import { jsonObjectSchema, jsonValueSchema, labelText, savedToolInvocationSchema, TheoremError, TOOL_AUTH_TYPES, TOOL_GATE_KINDS, TOOL_PERMISSION, toolOperationOutcomeSchema, turnHistoryMessageSchema, turnToolSnapshotSchema, z, } from '@theoremjs/agents';
12
+ import { gateExpired } from '@theoremjs/agents/kernel';
13
+ import { checkSessionLimits, resolveSessionLimits, } from "./session-limits.js";
14
+ export const DEFAULT_OPERATION_RETENTION_MS = 24 * 60 * 60 * 1000;
15
+ const blobSchema = z.strictObject({
16
+ data: z.string(),
17
+ mimeType: z.string(),
18
+ name: z.string().optional(),
19
+ });
20
+ const inputSchema = z.strictObject({
21
+ text: z.string().optional(),
22
+ role: z.string().optional(),
23
+ slots: z.record(z.string(), z.string()).optional(),
24
+ context: z
25
+ .strictObject({ client: jsonValueSchema.optional(), server: jsonValueSchema.optional() })
26
+ .optional(),
27
+ attachments: z
28
+ .array(z.union([
29
+ blobSchema,
30
+ z.strictObject({
31
+ uri: z.string(),
32
+ mimeType: z.string(),
33
+ name: z.string().optional(),
34
+ }),
35
+ ]))
36
+ .optional(),
37
+ voice: z.array(blobSchema).optional(),
38
+ history: z.array(turnHistoryMessageSchema).optional(),
39
+ repair: z
40
+ .strictObject({ previousOutput: z.string(), rejection: z.string(), guidance: z.string() })
41
+ .optional(),
42
+ historyTokens: z.number().nonnegative().optional(),
43
+ inputTokens: z.number().nonnegative().optional(),
44
+ });
45
+ const pendingSchema = z.strictObject({
46
+ profileId: z.string().min(1),
47
+ operationId: z.string().min(1).max(256),
48
+ callId: z.string().min(1),
49
+ name: z.string().min(1),
50
+ arguments: jsonObjectSchema,
51
+ gate: z
52
+ .strictObject({
53
+ kind: z.enum(TOOL_GATE_KINDS),
54
+ permission: z.enum(TOOL_PERMISSION).optional(),
55
+ auth: z
56
+ .strictObject({
57
+ slot: z.string().min(1),
58
+ authType: z.enum(TOOL_AUTH_TYPES),
59
+ service: z.string().min(1),
60
+ })
61
+ .optional(),
62
+ })
63
+ .superRefine((gate, ctx) => {
64
+ if ((gate.kind === 'auth') !== (gate.auth !== undefined)) {
65
+ ctx.addIssue({ code: 'custom', path: ['auth'] });
66
+ }
67
+ }),
68
+ snapshot: turnToolSnapshotSchema.optional(),
69
+ promoted: z.array(z.string().min(1)),
70
+ turnInput: inputSchema,
71
+ model: z.string().optional(),
72
+ createdAt: z.number().finite().nonnegative(),
73
+ expiresAt: z.number().finite().nonnegative().optional(),
74
+ });
75
+ const acceptedAnswerSchema = z.strictObject({
76
+ input: jsonObjectSchema,
77
+ resume: z.strictObject({
78
+ granted: z.boolean().optional(),
79
+ gateKind: z.enum(TOOL_GATE_KINDS).optional(),
80
+ cause: z.enum(['declined', 'abandoned', 'expired']).optional(),
81
+ signIn: z.boolean().optional(),
82
+ edited: z.strictObject({ from: jsonObjectSchema }).optional(),
83
+ }),
84
+ sessionPermissions: z.array(z.string().min(1)),
85
+ page: z
86
+ .union([
87
+ z.strictObject({ output: jsonValueSchema }),
88
+ z.strictObject({ timedOut: z.literal(true) }),
89
+ z.strictObject({ unanswered: z.literal(true) }),
90
+ ])
91
+ .optional(),
92
+ });
93
+ const identityFields = {
94
+ operationId: z.string().min(1).max(256),
95
+ callId: z.string().min(1),
96
+ profileId: z.string().min(1),
97
+ toolId: z.string().min(1),
98
+ invocationHash: z.string().min(1),
99
+ invocation: savedToolInvocationSchema.optional(),
100
+ };
101
+ const pageGrant = z.strictObject({
102
+ grantId: z.string().min(1),
103
+ expiresAt: z.number().finite().positive(),
104
+ resultHash: z.string().optional(),
105
+ });
106
+ const operationSchema = z.discriminatedUnion('status', [
107
+ z.strictObject({
108
+ status: z.literal('ready'),
109
+ profileId: z.string().min(1),
110
+ pending: pendingSchema,
111
+ answer: acceptedAnswerSchema,
112
+ readyAt: z.number().finite().nonnegative(),
113
+ }),
114
+ z.strictObject({
115
+ ...identityFields,
116
+ status: z.literal('running'),
117
+ pageGrant: pageGrant.optional(),
118
+ claimToken: z.string().min(1),
119
+ startedAt: z.number().finite().nonnegative(),
120
+ }),
121
+ z.strictObject({
122
+ ...identityFields,
123
+ status: z.literal('settled'),
124
+ pageGrant: pageGrant.optional(),
125
+ outcome: toolOperationOutcomeSchema,
126
+ settledAt: z.number().finite().nonnegative(),
127
+ retainUntil: z.number().finite().nonnegative().optional(),
128
+ }),
129
+ z.strictObject({
130
+ ...identityFields,
131
+ status: z.literal('uncertain'),
132
+ pageGrant: pageGrant.optional(),
133
+ claimToken: z.string().min(1),
134
+ reason: z.enum(['execution_interrupted', 'settlement_write_failed', 'worker_lost']),
135
+ detectedAt: z.number().finite().nonnegative(),
136
+ }),
137
+ ]);
138
+ /** Stored authority is checked before the handler reads or changes it. */
139
+ export const theoremSessionStateSchema = z
140
+ .strictObject({
141
+ version: z.literal(3),
142
+ operations: z.record(z.string().min(1), operationSchema),
143
+ permissions: z.array(z.string().min(1)),
144
+ gates: z.record(z.string().min(1), pendingSchema),
145
+ providerCheckpoints: z.array(z.string().min(1)),
146
+ })
147
+ .superRefine((state, ctx) => {
148
+ for (const [operationId, operation] of Object.entries(state.operations)) {
149
+ const reference = operation.status === 'ready' ? operation.pending : operation;
150
+ if (reference.operationId !== operationId ||
151
+ (operation.status === 'ready' && operation.profileId !== operation.pending.profileId) ||
152
+ operationId in state.gates ||
153
+ (operation.status === 'settled' &&
154
+ (operation.outcome.event.tool.operationId !== operationId ||
155
+ operation.outcome.event.tool.callId !== operation.callId ||
156
+ operation.outcome.event.tool.name !== operation.toolId))) {
157
+ ctx.addIssue({ code: 'custom', path: ['operations', operationId] });
158
+ }
159
+ }
160
+ for (const [operationId, gate] of Object.entries(state.gates)) {
161
+ if (operationId !== gate.operationId)
162
+ ctx.addIssue({ code: 'custom', path: ['gates', operationId] });
163
+ }
164
+ });
165
+ export function emptySessionState() {
166
+ return {
167
+ version: 3,
168
+ operations: {},
169
+ permissions: [],
170
+ gates: {},
171
+ providerCheckpoints: [],
172
+ };
173
+ }
174
+ /** Drop gates (waiting or settled) older than `ttlMs`. */
175
+ export function pruneGates(gates, now, ttlMs) {
176
+ return Object.fromEntries(Object.entries(gates).filter(([, gate]) => gate.expiresAt !== undefined
177
+ ? now < gate.expiresAt
178
+ : !gateExpired(gate.createdAt, now, ttlMs)));
179
+ }
180
+ export function memoryStoreLimits(options) {
181
+ const ttlMs = options.ttlMs ?? 24 * 60 * 60 * 1000;
182
+ const maxSessions = options.maxSessions ?? 10_000;
183
+ if (!Number.isFinite(ttlMs) ||
184
+ ttlMs <= 0 ||
185
+ !Number.isSafeInteger(maxSessions) ||
186
+ maxSessions <= 0) {
187
+ throw new TheoremError('config', labelText('error.config'), {
188
+ cause: { code: 'session_store_options', ttlMs, maxSessions },
189
+ });
190
+ }
191
+ return { ttlMs, maxSessions };
192
+ }
193
+ /** A per-session value kept in process memory: forgotten when idle, oldest evicted first. */
194
+ export function createMemorySessionMap(options = {}) {
195
+ const { ttlMs, maxSessions } = memoryStoreLimits(options);
196
+ const sessions = new Map();
197
+ return {
198
+ load(sessionId) {
199
+ const entry = sessions.get(sessionId);
200
+ if (!entry)
201
+ return undefined;
202
+ if (Date.now() - entry.touched > ttlMs) {
203
+ sessions.delete(sessionId);
204
+ return undefined;
205
+ }
206
+ return structuredClone(entry.value);
207
+ },
208
+ save(sessionId, value) {
209
+ sessions.delete(sessionId);
210
+ sessions.set(sessionId, { value: structuredClone(value), touched: Date.now() });
211
+ while (sessions.size > maxSessions) {
212
+ const oldest = sessions.keys().next().value;
213
+ if (oldest === undefined)
214
+ break;
215
+ sessions.delete(oldest);
216
+ }
217
+ },
218
+ };
219
+ }
220
+ export function createMemorySessionStore(options = {}) {
221
+ const { ttlMs, maxSessions } = memoryStoreLimits(options);
222
+ const limits = resolveSessionLimits(options.sessionLimits);
223
+ const sessions = new Map();
224
+ const held = (entry) => Object.values(entry.snapshot.state.gates).some((gate) => gate.expiresAt === undefined || Date.now() < gate.expiresAt) ||
225
+ Object.values(entry.snapshot.state.operations).some((operation) => operation.status !== 'settled' ||
226
+ Date.now() <
227
+ (operation.retainUntil ?? operation.settledAt + DEFAULT_OPERATION_RETENTION_MS));
228
+ const current = (sessionId) => {
229
+ const entry = sessions.get(sessionId);
230
+ if (entry && !held(entry) && Date.now() - entry.touched >= ttlMs) {
231
+ sessions.delete(sessionId);
232
+ return undefined;
233
+ }
234
+ return entry?.snapshot;
235
+ };
236
+ return {
237
+ load(sessionId) {
238
+ return Promise.resolve(structuredClone(current(sessionId)));
239
+ },
240
+ compareAndSwap(sessionId, expectedRevision, next) {
241
+ const snapshot = current(sessionId);
242
+ if ((snapshot?.revision ?? null) !== expectedRevision)
243
+ return Promise.resolve({ written: false });
244
+ const state = theoremSessionStateSchema.parse(next);
245
+ checkSessionLimits(state, limits);
246
+ if (!snapshot && sessions.size >= maxSessions) {
247
+ const removable = [...sessions].find(([, entry]) => !held(entry));
248
+ if (!removable) {
249
+ throw new TheoremError('unavailable', labelText('error.unavailable'), {
250
+ cause: { code: 'session_store_capacity' },
251
+ });
252
+ }
253
+ sessions.delete(removable[0]);
254
+ }
255
+ const revision = crypto.randomUUID();
256
+ sessions.delete(sessionId);
257
+ sessions.set(sessionId, {
258
+ snapshot: { revision, state: structuredClone(state) },
259
+ touched: Date.now(),
260
+ });
261
+ return Promise.resolve({ written: true, revision });
262
+ },
263
+ };
264
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The shape the decision and embedding handlers share: `GET <base>` answers the profile's
3
+ * interface, `POST <base>/<action>` answers `{ result }`, and a failure is worded with the
4
+ * profile's labels.
5
+ *
6
+ * @module
7
+ */
8
+ import { type ProfileDefinition } from '@theoremjs/agents';
9
+ import { type ClientTurnOptions } from '@theoremjs/agents/host';
10
+ export type SingleActionOptions = {
11
+ /** The last path segment the POST is sent to, such as `decide`. */
12
+ action: string;
13
+ /** What `GET <base>` answers, as `{ interface }`. */
14
+ iface: unknown;
15
+ labels: ProfileDefinition['labels'];
16
+ clientEvents?: ClientTurnOptions;
17
+ onError?: (err: unknown, ctx: {
18
+ request: Request;
19
+ }) => void;
20
+ };
21
+ export declare function singleActionHandler({ action, iface, labels, clientEvents, onError }: SingleActionOptions, run: (request: Request) => Promise<unknown>): (request: Request) => Promise<Response>;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The shape the decision and embedding handlers share: `GET <base>` answers the profile's
3
+ * interface, `POST <base>/<action>` answers `{ result }`, and a failure is worded with the
4
+ * profile's labels.
5
+ *
6
+ * @module
7
+ */
8
+ import { labelText } from '@theoremjs/agents';
9
+ import { caughtStatus, clientError, HTTP_METHOD, } from '@theoremjs/agents/host';
10
+ import { jsonResponse } from "./json-response.js";
11
+ export function singleActionHandler({ action, iface, labels, clientEvents, onError }, run) {
12
+ const isAction = (request) => new URL(request.url).pathname.split('/').filter(Boolean).at(-1) === action;
13
+ return async (request) => {
14
+ try {
15
+ if (request.method === 'GET' && !isAction(request))
16
+ return jsonResponse(200, { interface: iface });
17
+ if (request.method !== 'POST' || !isAction(request)) {
18
+ return jsonResponse(HTTP_METHOD, {
19
+ error: labelText('error.request', {}, labels),
20
+ errorKind: 'request',
21
+ });
22
+ }
23
+ return jsonResponse(200, { result: await run(request) });
24
+ }
25
+ catch (err) {
26
+ onError?.(err, { request });
27
+ return jsonResponse(caughtStatus(err), clientError(err, labels, clientEvents));
28
+ }
29
+ };
30
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Mid-turn steer inbox — the client posts user messages while a turn streams;
3
+ * the turn's stage handler drains one unit per inject-capable stage.
4
+ *
5
+ * @module
6
+ */
7
+ import { type StageHandler, type TurnHistoryMessage } from '@theoremjs/agents';
8
+ import type { TheoremSteerRequest } from '../client/transport.ts';
9
+ /** One steer: the client's id for it, reported back in `stage.injected` once it lands. */
10
+ export type SteerUnit = {
11
+ id: string;
12
+ messages: TurnHistoryMessage[];
13
+ };
14
+ /**
15
+ * A steer as a client posts it (a checked `TheoremSteerRequest`): its user
16
+ * messages. Other roles are dropped — a client never injects system,
17
+ * assistant or tool turns — and a steer with no user message is refused.
18
+ */
19
+ export declare function steerUnitOf(steer: Pick<TheoremSteerRequest, 'id' | 'inject'>): SteerUnit;
20
+ /** Lands the inbox's next steer, one per steerable stage, named by its id. */
21
+ export declare function steerStage(inbox: SteerInbox, key: string): StageHandler;
22
+ /**
23
+ * Keyed by client turn id. The default lives in process memory, which is only
24
+ * correct when steer POSTs reach the same process as the turn — swap in a
25
+ * shared store (KV, Redis, Durable Object) for multi-instance hosts.
26
+ */
27
+ export interface SteerInbox {
28
+ open(turnId: string): void | Promise<void>;
29
+ /** Returns `false` when no turn with this id is open. */
30
+ enqueue(turnId: string, unit: SteerUnit): boolean | Promise<boolean>;
31
+ /** Next unit, FIFO, or `undefined`. */
32
+ consume(turnId: string): SteerUnit | undefined | Promise<SteerUnit | undefined>;
33
+ close(turnId: string): void | Promise<void>;
34
+ }
35
+ export declare function createMemorySteerInbox(): SteerInbox;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Mid-turn steer inbox — the client posts user messages while a turn streams;
3
+ * the turn's stage handler drains one unit per inject-capable stage.
4
+ *
5
+ * @module
6
+ */
7
+ import { TheoremError } from '@theoremjs/agents';
8
+ /** The stages a steer can land at. */
9
+ const STEER_STAGES = new Set(['pre_turn', 'post_tool', 'before_end']);
10
+ /**
11
+ * A steer as a client posts it (a checked `TheoremSteerRequest`): its user
12
+ * messages. Other roles are dropped — a client never injects system,
13
+ * assistant or tool turns — and a steer with no user message is refused.
14
+ */
15
+ export function steerUnitOf(steer) {
16
+ const messages = steer.inject.filter((message) => message.role === 'user');
17
+ // labels-exempt: internal diagnostic; the user reads the error kind's (or copy key's) wording
18
+ if (!messages.length)
19
+ throw new TheoremError('request', 'inject must contain user messages');
20
+ return { id: steer.id, messages };
21
+ }
22
+ /** Lands the inbox's next steer, one per steerable stage, named by its id. */
23
+ export function steerStage(inbox, key) {
24
+ return async ({ stage }) => {
25
+ if (!STEER_STAGES.has(stage))
26
+ return;
27
+ const unit = await inbox.consume(key);
28
+ return unit ? { inject: unit.messages, injectId: unit.id } : undefined;
29
+ };
30
+ }
31
+ export function createMemorySteerInbox() {
32
+ const queues = new Map();
33
+ return {
34
+ open(turnId) {
35
+ if (!queues.has(turnId))
36
+ queues.set(turnId, []);
37
+ },
38
+ enqueue(turnId, unit) {
39
+ const queue = queues.get(turnId);
40
+ if (!queue)
41
+ return false;
42
+ queue.push(structuredClone(unit));
43
+ return true;
44
+ },
45
+ consume(turnId) {
46
+ return queues.get(turnId)?.shift();
47
+ },
48
+ close(turnId) {
49
+ queues.delete(turnId);
50
+ },
51
+ };
52
+ }
@@ -0,0 +1,6 @@
1
+ import { type ToolExecutionCoordinator } from '@theoremjs/agents';
2
+ import type { TheoremSessionState } from './session-store.ts';
3
+ export declare function sessionToolCoordinator(store: {
4
+ mutate<T>(id: string, change: (state: TheoremSessionState) => T): Promise<T>;
5
+ }, sessionId: string): ToolExecutionCoordinator;
6
+ export declare function sameAcceptedAnswer(left: unknown, right: unknown): boolean;
@@ -0,0 +1,132 @@
1
+ import { labelText, TheoremError, } from '@theoremjs/agents';
2
+ function unavailable() {
3
+ throw new TheoremError('unavailable', labelText('error.unavailable'));
4
+ }
5
+ function sameOperation(record, identity) {
6
+ return (record.operationId === identity.operationId &&
7
+ record.callId === identity.callId &&
8
+ record.profileId === identity.profileId &&
9
+ record.toolId === identity.toolId &&
10
+ record.invocationHash === identity.invocationHash);
11
+ }
12
+ function checkedRecord(state, identity) {
13
+ if (identity.operationId === '__proto__')
14
+ unavailable();
15
+ const record = Object.hasOwn(state.operations, identity.operationId)
16
+ ? state.operations[identity.operationId]
17
+ : undefined;
18
+ if (record?.status === 'ready') {
19
+ if (record.pending.callId !== identity.callId ||
20
+ record.pending.profileId !== identity.profileId ||
21
+ record.pending.name !== identity.toolId ||
22
+ record.profileId !== identity.profileId)
23
+ unavailable();
24
+ }
25
+ else if (record && !sameOperation(record, identity))
26
+ unavailable();
27
+ return record;
28
+ }
29
+ function checkedClaim(state, claim) {
30
+ const record = checkedRecord(state, claim);
31
+ if (!record ||
32
+ (record.status !== 'running' && record.status !== 'uncertain') ||
33
+ record.claimToken !== claim.claimToken)
34
+ unavailable();
35
+ return record;
36
+ }
37
+ function existingResult(record) {
38
+ if (record?.status === 'settled')
39
+ return { kind: 'settled', outcome: record.outcome };
40
+ if (record?.status === 'running')
41
+ return { kind: 'running' };
42
+ if (record?.status === 'uncertain')
43
+ return { kind: 'uncertain' };
44
+ return undefined;
45
+ }
46
+ function saveOutcome(state, identity, outcome) {
47
+ const before = state.operations[identity.operationId];
48
+ const pageGrant = before && before.status !== 'ready' ? before.pageGrant : undefined;
49
+ const settledAt = Date.now();
50
+ const { operationId, callId, profileId, toolId, invocationHash, invocation } = identity;
51
+ state.operations[identity.operationId] = {
52
+ operationId,
53
+ callId,
54
+ profileId,
55
+ toolId,
56
+ invocationHash,
57
+ ...(invocation ? { invocation } : {}),
58
+ status: 'settled',
59
+ ...(pageGrant ? { pageGrant } : {}),
60
+ outcome,
61
+ settledAt,
62
+ };
63
+ }
64
+ export function sessionToolCoordinator(store, sessionId) {
65
+ return {
66
+ lookup(identity) {
67
+ return store.mutate(sessionId, (state) => {
68
+ const record = checkedRecord(state, identity);
69
+ return existingResult(record);
70
+ });
71
+ },
72
+ begin(identity) {
73
+ const claimToken = crypto.randomUUID();
74
+ const startedAt = Date.now();
75
+ return store.mutate(sessionId, (state) => {
76
+ const record = checkedRecord(state, identity);
77
+ const existing = existingResult(record);
78
+ if (existing)
79
+ return existing;
80
+ state.operations[identity.operationId] = {
81
+ ...identity,
82
+ status: 'running',
83
+ claimToken,
84
+ startedAt,
85
+ };
86
+ return { kind: 'acquired', claimToken };
87
+ });
88
+ },
89
+ settle(claim, outcome) {
90
+ return store.mutate(sessionId, (state) => {
91
+ const record = checkedClaim(state, claim);
92
+ saveOutcome(state, { ...claim, invocation: record.invocation }, outcome);
93
+ });
94
+ },
95
+ settleNotStarted(identity, outcome) {
96
+ return store.mutate(sessionId, (state) => {
97
+ const record = checkedRecord(state, identity);
98
+ if (record && record.status !== 'ready')
99
+ unavailable();
100
+ if (outcome.effects !== 'not_started')
101
+ unavailable();
102
+ saveOutcome(state, identity, outcome);
103
+ });
104
+ },
105
+ markUncertain(claim, reason) {
106
+ return store.mutate(sessionId, (state) => {
107
+ const record = checkedClaim(state, claim);
108
+ state.operations[claim.operationId] = {
109
+ ...claim,
110
+ invocation: record.invocation,
111
+ ...(record.pageGrant ? { pageGrant: record.pageGrant } : {}),
112
+ status: 'uncertain',
113
+ reason,
114
+ detectedAt: Date.now(),
115
+ };
116
+ });
117
+ },
118
+ };
119
+ }
120
+ export function sameAcceptedAnswer(left, right) {
121
+ const canonical = (value) => {
122
+ if (Array.isArray(value))
123
+ return value.map(canonical);
124
+ if (value && typeof value === 'object')
125
+ return Object.fromEntries(Object.entries(value)
126
+ .filter(([, item]) => item !== undefined)
127
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
128
+ .map(([key, item]) => [key, canonical(item)]));
129
+ return value;
130
+ };
131
+ return JSON.stringify(canonical(left)) === JSON.stringify(canonical(right));
132
+ }
@@ -0,0 +1,10 @@
1
+ import type { SessionRequest, TurnInput } from '@theoremjs/agents';
2
+ import type { LiveOpenMessage } from '../client/live-messages.ts';
3
+ import type { TheoremTurnInput } from '../client/transport.ts';
4
+ /** A turn input off the wire as the kernel takes it: the page's context is the client's. */
5
+ export declare function kernelTurnInput(input: TheoremTurnInput, server?: unknown): TurnInput;
6
+ /**
7
+ * What a live call's first message asks of the session, as `runSession` takes
8
+ * it: the page's context is the client's, and `server` is the host's own.
9
+ */
10
+ export declare function liveSessionOpen(open: LiveOpenMessage, server?: unknown): Pick<SessionRequest, 'slots' | 'context' | 'providerState' | 'awayMs' | 'history'>;