@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,88 +0,0 @@
1
- import { labelText, TheoremError } from '@theoremjs/agents';
2
- import { foldTurnEvents, prepareUserTurn, streamThoughtsEnabled, } from '@theoremjs/agents/interface';
3
- import { filesToPending } from "./encode-files.js";
4
- import { defaultModel } from "./generation-selection.js";
5
- export function issuedOperationId(call) {
6
- if (!call.operationId)
7
- throw new TheoremError('request', labelText('session.gate_expired'));
8
- return call.operationId;
9
- }
10
- function resolveModelId(iface, session) {
11
- return session.selectedModel ?? defaultModel(iface);
12
- }
13
- function resolveEffort(iface, session, modelId) {
14
- if (!modelId || !Object.hasOwn(iface.models, modelId))
15
- return undefined;
16
- const binding = iface.models[modelId];
17
- if (!binding.allowEffortSelect)
18
- return undefined;
19
- return session.selectedEffort ?? binding.defaultEffort;
20
- }
21
- /** Model / effort selection the host should honour for this session. */
22
- function generationFields(iface, session) {
23
- const modelId = resolveModelId(iface, session);
24
- const model = iface.allowModelSelect ? modelId : undefined;
25
- const effort = resolveEffort(iface, session, modelId);
26
- return { ...(model ? { model } : {}), ...(effort ? { effort } : {}) };
27
- }
28
- function gateReplay(iface, session, call) {
29
- return {
30
- name: call.name,
31
- input: call.input,
32
- ...(call.gateKind ? { gateKind: call.gateKind } : {}),
33
- sessionPermissions: call.sessionPermissions ?? session.sessionPermissions,
34
- ...(session.toolSnapshot ? { snapshot: session.toolSnapshot } : {}),
35
- ...(session.promotedToolIds.length ? { promoted: [...session.promotedToolIds] } : {}),
36
- ...generationFields(iface, session),
37
- };
38
- }
39
- function scopeFields(scope) {
40
- return {
41
- ...(scope.conversation ? { conversation: scope.conversation } : {}),
42
- ...(scope.turnId ? { turnId: scope.turnId } : {}),
43
- };
44
- }
45
- export function buildTurnRequest(iface, session, input, options = {}) {
46
- const abandon = (options.walkAway?.calls ?? []).map((call) => ({
47
- callId: call.callId,
48
- operationId: issuedOperationId(call),
49
- }));
50
- return {
51
- ...scopeFields(options),
52
- ...generationFields(iface, session),
53
- input,
54
- ...(abandon.length ? { abandon } : {}),
55
- replay: { sessionPermissions: session.sessionPermissions },
56
- };
57
- }
58
- export function buildInvokeRequest(iface, session, args, scope = {}) {
59
- return {
60
- ...scopeFields(scope),
61
- gateId: args.gateId,
62
- operationId: args.operationId,
63
- decision: args.decision,
64
- ...(args.secret === undefined ? {} : { secret: args.secret }),
65
- ...(args.page === undefined ? {} : { page: args.page }),
66
- replay: gateReplay(iface, session, {
67
- ...args,
68
- gateKind: args.gateKind ??
69
- (session.gatedTool?.callId === args.gateId ? session.gatedTool.gateKind : undefined),
70
- }),
71
- };
72
- }
73
- export function projectUserTurn(iface, draft) {
74
- return prepareUserTurn(iface.inputs, draft, iface.guardrails);
75
- }
76
- export function prepareComposerTurn(iface, text, pendingFiles, pendingVoice = []) {
77
- return projectUserTurn(iface, {
78
- ...(text.trim() ? { text } : {}),
79
- ...(pendingFiles.length ? { attachments: filesToPending(pendingFiles) } : {}),
80
- ...(pendingVoice.length ? { voice: filesToPending(pendingVoice) } : {}),
81
- });
82
- }
83
- export function foldAssistantTurn(iface, events) {
84
- // why: Its `turn-done` blocks stay: hidden in the view, they carry the reply's worked time.
85
- return foldTurnEvents(events, {
86
- showThoughts: streamThoughtsEnabled(iface.outputs),
87
- });
88
- }
@@ -1,56 +0,0 @@
1
- /**
2
- * Every value that reaches the browser over a wire (NDJSON lines, live
3
- * envelopes) is checked against its schema here. A line or event of a kind the
4
- * client does not know is reported as `unsupported`, and one that fails its
5
- * check (not JSON, no kind, or a known kind that fails its schema) as
6
- * `malformed`; either way the reply goes on. A whole reply that fails its check
7
- * (the profile description) is `bad_response`.
8
- *
9
- * @module
10
- */
11
- import { TheoremError, z } from '@theoremjs/agents';
12
- /**
13
- * A line or envelope of a kind the client does not know. The server never
14
- * sends it; the client's parser makes it so the host sees what arrived. Theorem
15
- * shows nothing for it.
16
- */
17
- export type UnsupportedEvent = {
18
- type: 'unsupported';
19
- received: string;
20
- raw: unknown;
21
- };
22
- /**
23
- * A line or event that failed its wire check. The server never sends it; the
24
- * client's reader makes it, leaves the line out, and the reply goes on. `error`
25
- * is `bad_response`: its message names what broke, never the value, and the
26
- * user reads `session.part_skipped`.
27
- */
28
- export type MalformedEvent = {
29
- type: 'malformed';
30
- error: TheoremError;
31
- };
32
- /** Every kind a wire carries, and the schema a value of that kind must pass. */
33
- export type WireLines<Line extends {
34
- type: string;
35
- }> = {
36
- readonly [K in Line['type']]: z.ZodType<Extract<Line, {
37
- type: K;
38
- }>>;
39
- };
40
- /**
41
- * Issue paths and codes only: a failure names what broke, never the value. A
42
- * union that matched no branch lists each branch's issues.
43
- */
44
- export declare function issueSummary(error: z.ZodError): string;
45
- /** `raw` as `schema` reads it; one that fails is `bad_response`, naming `what`. */
46
- export declare function checkWire<T>(schema: z.ZodType<T>, raw: unknown, what: string): T;
47
- /** One wire value: a known kind that passed its schema, `unsupported`, or `malformed`. */
48
- export declare function readWireValue<Line extends {
49
- type: string;
50
- }>(lines: WireLines<Line>, raw: unknown): Line | UnsupportedEvent | MalformedEvent;
51
- /** One wire line's text: {@link readWireValue} of its JSON, or `malformed` when it is not JSON. */
52
- export declare function readWireLine<Line extends {
53
- type: string;
54
- }>(lines: WireLines<Line>, text: string): Line | UnsupportedEvent | MalformedEvent;
55
- /** A whole reply's JSON text as a value; text that is not JSON is `bad_response`. */
56
- export declare function parseWireJson(text: string): unknown;
@@ -1,90 +0,0 @@
1
- /**
2
- * Every value that reaches the browser over a wire (NDJSON lines, live
3
- * envelopes) is checked against its schema here. A line or event of a kind the
4
- * client does not know is reported as `unsupported`, and one that fails its
5
- * check (not JSON, no kind, or a known kind that fails its schema) as
6
- * `malformed`; either way the reply goes on. A whole reply that fails its check
7
- * (the profile description) is `bad_response`.
8
- *
9
- * @module
10
- */
11
- import { TheoremError, z } from '@theoremjs/agents';
12
- const wireKind = z.object({ type: z.string() });
13
- function isKnown(lines, type) {
14
- return Object.hasOwn(lines, type);
15
- }
16
- function issuesSummary(issues, at) {
17
- return issues
18
- .map((issue) => {
19
- const path = [...at, ...issue.path];
20
- const head = `${path.map(String).join('.') || '(root)'} ${issue.code}`;
21
- if (issue.code !== 'invalid_union' || issue.errors.length === 0)
22
- return head;
23
- return `${head} [${issue.errors.map((branch) => issuesSummary(branch, path)).join(' | ')}]`;
24
- })
25
- .join('; ');
26
- }
27
- /**
28
- * Issue paths and codes only: a failure names what broke, never the value. A
29
- * union that matched no branch lists each branch's issues.
30
- */
31
- export function issueSummary(error) {
32
- return issuesSummary(error.issues, []);
33
- }
34
- function badResponse(what, error) {
35
- // labels-exempt: internal diagnostic; the user reads error.bad_response
36
- return new TheoremError('bad_response', `${what} failed its wire check: ${issueSummary(error)}`);
37
- }
38
- /** `raw` as `schema` reads it; one that fails is `bad_response`, naming `what`. */
39
- export function checkWire(schema, raw, what) {
40
- const parsed = schema.safeParse(raw);
41
- if (!parsed.success)
42
- throw badResponse(what, parsed.error);
43
- return parsed.data;
44
- }
45
- function malformed(message) {
46
- return {
47
- type: 'malformed',
48
- error: new TheoremError('bad_response', message, { copy: { key: 'session.part_skipped' } }),
49
- };
50
- }
51
- /** One wire value: a known kind that passed its schema, `unsupported`, or `malformed`. */
52
- export function readWireValue(lines, raw) {
53
- const kind = wireKind.safeParse(raw);
54
- // labels-exempt: internal diagnostic; the user reads session.part_skipped
55
- if (!kind.success)
56
- return malformed(`a line without a kind failed its wire check: ${issueSummary(kind.error)}`);
57
- const { type } = kind.data;
58
- if (!isKnown(lines, type))
59
- return { type: 'unsupported', received: type, raw };
60
- const line = lines[type].safeParse(raw);
61
- if (line.success)
62
- return line.data;
63
- // labels-exempt: internal diagnostic; the user reads session.part_skipped
64
- return malformed(`a '${type}' line failed its wire check: ${issueSummary(line.error)}`);
65
- }
66
- /** JSON text as a value, or undefined when it is not JSON. The parser's own error quotes the text, so it is not kept. */
67
- function jsonValue(text) {
68
- try {
69
- return { value: JSON.parse(text) };
70
- }
71
- catch (err) {
72
- if (err instanceof SyntaxError)
73
- return undefined;
74
- throw err;
75
- }
76
- }
77
- /** One wire line's text: {@link readWireValue} of its JSON, or `malformed` when it is not JSON. */
78
- export function readWireLine(lines, text) {
79
- const json = jsonValue(text);
80
- // labels-exempt: internal diagnostic; the user reads session.part_skipped
81
- return json ? readWireValue(lines, json.value) : malformed('a line is not JSON');
82
- }
83
- /** A whole reply's JSON text as a value; text that is not JSON is `bad_response`. */
84
- export function parseWireJson(text) {
85
- const json = jsonValue(text);
86
- // labels-exempt: internal diagnostic; the user reads error.bad_response
87
- if (!json)
88
- throw new TheoremError('bad_response', 'a line is not JSON');
89
- return json.value;
90
- }
@@ -1,16 +0,0 @@
1
- /**
2
- * AudioWorklet processor that forwards mono mic frames to the main thread.
3
- * Plain JS: it runs in the AudioWorkletGlobalScope as its own module, loaded
4
- * by URL (see `../mic-capture.ts`), so it imports nothing and ships as is.
5
- */
6
- registerProcessor(
7
- 'mic-capture-processor',
8
- class MicCaptureProcessor extends AudioWorkletProcessor {
9
- process(inputs) {
10
- const channel = inputs[0][0];
11
- const copy = new Float32Array(channel);
12
- this.port.postMessage(copy, [copy.buffer]);
13
- return true;
14
- }
15
- },
16
- );
@@ -1,79 +0,0 @@
1
- import type { AttachmentValidationIssue, ComposerMenuAction, ComposerPendingMessage, ComposerProfileInterface, ComposerRunPhase, InterfaceTurnSession, TranscriptBlock } from '@theoremjs/agents/interface';
2
- import { type MutableRefObject } from 'react';
3
- import { type ClientFailure } from '../client/failure.ts';
4
- import { type AnsweringGate, type StreamView, type ToolDecisionAction, type ToolGateResolution } from '../client/index.ts';
5
- import type { RunOutcome } from '../client/run-session.ts';
6
- import type { TheoremTransport } from '../client/transport.ts';
7
- import type { MessageDelivery } from './use-theorem-chat-state.ts';
8
- export type RunTurnStream = (run: (view: StreamView) => Promise<RunOutcome>, options?: {
9
- /** The run sends a message: one that never posts goes back in the composer with its failure. */
10
- message?: {
11
- restore(): Promise<void>;
12
- };
13
- }) => Promise<void>;
14
- export type TheoremChatActionArgs = {
15
- iface: ComposerProfileInterface | null;
16
- transport: TheoremTransport;
17
- phase: ComposerRunPhase;
18
- gated: boolean;
19
- draftText: string;
20
- pendingFiles: File[];
21
- pendingVoice: File[];
22
- /** The composer as it is now, for a run that ends after it changed. */
23
- composerRef: MutableRefObject<{
24
- draftText: string;
25
- pendingFiles: File[];
26
- pendingVoice: File[];
27
- }>;
28
- /** The ref of the conversation the chat continues; none before its first message posts. */
29
- conversationRef: MutableRefObject<string | undefined>;
30
- clearComposer: () => void;
31
- runTurnStream: RunTurnStream;
32
- sessionRef: MutableRefObject<InterfaceTurnSession>;
33
- blocksRef: MutableRefObject<TranscriptBlock[]>;
34
- abortRef: MutableRefObject<AbortController | null>;
35
- turnIdRef: MutableRefObject<string | null>;
36
- busyRef: MutableRefObject<boolean>;
37
- runPromiseRef: MutableRefObject<Promise<void> | null>;
38
- allowQueueDrainRef: MutableRefObject<boolean>;
39
- setBlocks: (value: TranscriptBlock[] | ((prev: TranscriptBlock[]) => TranscriptBlock[])) => void;
40
- setStreamBlocks: (value: TranscriptBlock[]) => void;
41
- setSession: (value: InterfaceTurnSession) => void;
42
- setChatStarted: (value: boolean) => void;
43
- setStreaming: (value: boolean) => void;
44
- setPendingMessages: (value: ComposerPendingMessage[] | ((prev: ComposerPendingMessage[]) => ComposerPendingMessage[])) => void;
45
- setDraftText: (value: string) => void;
46
- setPendingFiles: (value: File[]) => void;
47
- setPendingVoice: (value: File[]) => void;
48
- setIssues: (value: AttachmentValidationIssue[]) => void;
49
- setFailure: (value: ClientFailure | null) => void;
50
- setAnswering: (value: AnsweringGate | null) => void;
51
- setDelivery: (value: {
52
- status: MessageDelivery;
53
- } | null) => void;
54
- pendingRef: MutableRefObject<ComposerPendingMessage[]>;
55
- };
56
- type ComposerFields = {
57
- text: string;
58
- files: File[];
59
- voice: File[];
60
- };
61
- export declare function useTheoremChatActions(args: TheoremChatActionArgs): {
62
- startTurnFromFields: (fields: ComposerFields) => Promise<void>;
63
- startTurnFromDraft: (draft: ComposerPendingMessage["draft"], options?: {
64
- /** Send while the reply waits on gates: the message walks away from them. */
65
- walkAway?: boolean;
66
- /** Once the message posts: it leaves wherever it was waiting to be sent. */
67
- onPosted?: () => void;
68
- }) => Promise<void>;
69
- handleStop: () => void;
70
- handleSubmit: () => Promise<void>;
71
- handleSendNow: (draftSource?: ComposerPendingMessage) => Promise<void>;
72
- handleMenuAction: (action: ComposerMenuAction) => void;
73
- handleToolDecision: (_index: number, action: ToolDecisionAction) => Promise<void>;
74
- handleAuthenticated: (_index: number, secret?: string) => Promise<void>;
75
- resumeGatedTool: (resolution: ToolGateResolution) => Promise<void>;
76
- handlePendingRestore: (id: string) => Promise<void>;
77
- enqueuePending: (kind: "queue" | "steer" | "stash") => Promise<void>;
78
- };
79
- export {};
@@ -1,287 +0,0 @@
1
- import { TheoremError } from '@theoremjs/agents';
2
- import { convertSteersToFrontQueued, createComposerPendingMessage, orderComposerPendingMessages, removeComposerPendingMessage, userDraftHasPayload, userDraftToSteerInject, } from '@theoremjs/agents/interface';
3
- import { useCallback } from 'react';
4
- import { clientFailure } from "../client/failure.js";
5
- import { composerFieldsFromDraft, encodeComposerDraft, resumeInterfaceTool, streamInterfaceDraftTurn, streamInterfaceTurn, } from "../client/index.js";
6
- import { issuedOperationId } from "../client/turn-client.js";
7
- function composerFieldsPayload(text, pendingFiles, pendingVoice) {
8
- const meta = (files) => files.map((f) => ({
9
- name: f.name,
10
- mimeType: f.type || 'application/octet-stream',
11
- sizeBytes: f.size,
12
- }));
13
- return {
14
- ...(text.trim() ? { text } : {}),
15
- ...(pendingFiles.length ? { attachments: meta(pendingFiles) } : {}),
16
- ...(pendingVoice.length ? { voice: meta(pendingVoice) } : {}),
17
- };
18
- }
19
- function newTurnId() {
20
- return typeof crypto !== 'undefined' && 'randomUUID' in crypto
21
- ? crypto.randomUUID()
22
- : `turn-${Date.now()}`;
23
- }
24
- function beginAbortableTurn(args) {
25
- if (!args.iface)
26
- return null;
27
- const controller = new AbortController();
28
- args.abortRef.current = controller;
29
- const turnId = newTurnId();
30
- args.turnIdRef.current = turnId;
31
- return {
32
- composer: args.iface,
33
- turnId,
34
- signal: controller.signal,
35
- };
36
- }
37
- /** The run's scope: the conversation the chat continues, and the run's own turn. */
38
- function scopeOf(args, turnId) {
39
- return { conversation: args.conversationRef.current, turnId };
40
- }
41
- /**
42
- * Puts a message back in the composer. What the composer holds now is
43
- * stashed first, so nothing the user typed is lost.
44
- */
45
- function useRestoreToComposer(args) {
46
- return useCallback(async (fields) => {
47
- const { draftText, pendingFiles, pendingVoice } = args.composerRef.current;
48
- if (userDraftHasPayload(composerFieldsPayload(draftText, pendingFiles, pendingVoice))) {
49
- const draft = await encodeComposerDraft({ text: draftText, pendingFiles, pendingVoice });
50
- const stash = createComposerPendingMessage({ kind: 'stash', draft });
51
- args.setPendingMessages((prev) => orderComposerPendingMessages([...prev, stash]));
52
- }
53
- args.setDraftText(fields.text);
54
- args.setPendingFiles(fields.files);
55
- args.setPendingVoice(fields.voice);
56
- args.setIssues([]);
57
- }, [args]);
58
- }
59
- /** Starts a turn from the composer's fields, or from an encoded draft (queued or send-now). */
60
- function useTurnStarters(args, restoreToComposer) {
61
- const startTurnFromFields = useCallback(async (fields) => {
62
- const started = beginAbortableTurn(args);
63
- if (!started)
64
- return;
65
- await args.runTurnStream((view) => streamInterfaceTurn({
66
- iface: started.composer,
67
- transport: args.transport,
68
- session: args.sessionRef.current,
69
- text: fields.text,
70
- pendingFiles: fields.files,
71
- pendingVoice: fields.voice,
72
- signal: started.signal,
73
- scope: scopeOf(args, started.turnId),
74
- view,
75
- transcript: args.blocksRef.current,
76
- onUserBlocks: (userBlocks) => {
77
- args.setBlocks((prev) => [...prev, ...userBlocks]);
78
- args.setDelivery({ status: 'sending' });
79
- args.setChatStarted(true);
80
- args.setStreaming(true);
81
- args.clearComposer();
82
- },
83
- }), { message: { restore: () => restoreToComposer(fields) } });
84
- }, [args, restoreToComposer]);
85
- const startTurnFromDraft = useCallback(async (draft, options = {}) => {
86
- const started = beginAbortableTurn(args);
87
- if (!started)
88
- return;
89
- await args.runTurnStream((view) => streamInterfaceDraftTurn({
90
- iface: started.composer,
91
- transport: args.transport,
92
- session: args.sessionRef.current,
93
- draft,
94
- signal: started.signal,
95
- scope: scopeOf(args, started.turnId),
96
- view,
97
- transcript: args.blocksRef.current,
98
- walkAway: options.walkAway === true,
99
- onUserBlocks: (posted) => {
100
- args.setBlocks((prev) => [...prev, ...posted]);
101
- args.setStreamBlocks([]);
102
- args.setDelivery({ status: 'sending' });
103
- args.setChatStarted(true);
104
- args.setStreaming(true);
105
- options.onPosted?.();
106
- },
107
- }), { message: { restore: () => restoreToComposer(composerFieldsFromDraft(draft)) } });
108
- }, [args, restoreToComposer]);
109
- return { startTurnFromFields, startTurnFromDraft };
110
- }
111
- /** A steer with no running turn: the user reads `session.turn_ended`; queued steers move to the front. */
112
- function steerAfterTurnEnded(args, messageId) {
113
- args.setFailure(clientFailure(
114
- // labels-exempt: internal diagnostic; the user reads session.turn_ended
115
- new TheoremError('request', 'steer: no active turn', { copy: { key: 'session.turn_ended' } }), args.iface?.labels));
116
- args.setPendingMessages((prev) => orderComposerPendingMessages(convertSteersToFrontQueued(removeComposerPendingMessage(prev, messageId))));
117
- }
118
- /** Queue / steer / stash the composer draft, and restore a pending message into the composer. */
119
- function usePendingActions(args, restoreToComposer) {
120
- const enqueuePending = useCallback(async (kind) => {
121
- if (!userDraftHasPayload(composerFieldsPayload(args.draftText, args.pendingFiles, args.pendingVoice))) {
122
- return;
123
- }
124
- args.setIssues([]);
125
- try {
126
- const draft = await encodeComposerDraft({
127
- text: args.draftText,
128
- pendingFiles: args.pendingFiles,
129
- pendingVoice: args.pendingVoice,
130
- });
131
- const message = createComposerPendingMessage({ kind, draft });
132
- args.setPendingMessages((prev) => orderComposerPendingMessages([...prev, message]));
133
- args.clearComposer();
134
- if (kind !== 'steer')
135
- return;
136
- const turnId = args.turnIdRef.current;
137
- if (!turnId) {
138
- steerAfterTurnEnded(args, message.id);
139
- return;
140
- }
141
- const inject = userDraftToSteerInject(draft);
142
- if (inject.length === 0)
143
- return;
144
- await args.transport.steer({ turnId, id: message.id, inject });
145
- }
146
- catch (err) {
147
- args.setFailure(clientFailure(err, args.iface?.labels));
148
- }
149
- }, [args]);
150
- const handlePendingRestore = useCallback(async (id) => {
151
- const message = args.pendingRef.current.find((m) => m.id === id);
152
- if (!message)
153
- return;
154
- try {
155
- await restoreToComposer(composerFieldsFromDraft(message.draft));
156
- args.setPendingMessages((prev) => removeComposerPendingMessage(prev, id));
157
- }
158
- catch (err) {
159
- args.setFailure(clientFailure(err, args.iface?.labels));
160
- }
161
- }, [args, restoreToComposer]);
162
- return { enqueuePending, handlePendingRestore };
163
- }
164
- /** Answer the gate the session waits on: a decision, or a sign-in. */
165
- function useGateActions(args) {
166
- const resumeGatedTool = useCallback(async (resolution) => {
167
- const composer = args.iface;
168
- if (!composer)
169
- return;
170
- // why: Set once the run starts (not while another runs), and cleared once it has ended.
171
- let started = false;
172
- try {
173
- await args.runTurnStream((view) => {
174
- started = true;
175
- const session = args.sessionRef.current;
176
- // why: A person's answer shows on its gate while it settles; the page's has no gate to show on.
177
- if (session.gatedTool && resolution.action !== 'page')
178
- args.setAnswering({
179
- callId: session.gatedTool.callId,
180
- operationId: issuedOperationId(session.gatedTool),
181
- action: resolution.action,
182
- });
183
- return resumeInterfaceTool({
184
- iface: composer,
185
- transport: args.transport,
186
- session,
187
- scope: scopeOf(args),
188
- resolution,
189
- view,
190
- });
191
- });
192
- }
193
- finally {
194
- if (started)
195
- args.setAnswering(null);
196
- }
197
- }, [args]);
198
- const handleToolDecision = useCallback(async (_index, action) => {
199
- await resumeGatedTool({ action });
200
- }, [resumeGatedTool]);
201
- const handleAuthenticated = useCallback(async (_index, secret) => {
202
- await resumeGatedTool({ action: 'auth', ...(secret === undefined ? {} : { secret }) });
203
- }, [resumeGatedTool]);
204
- return { handleToolDecision, handleAuthenticated, resumeGatedTool };
205
- }
206
- export function useTheoremChatActions(args) {
207
- const restoreToComposer = useRestoreToComposer(args);
208
- const { startTurnFromFields, startTurnFromDraft } = useTurnStarters(args, restoreToComposer);
209
- const { enqueuePending, handlePendingRestore } = usePendingActions(args, restoreToComposer);
210
- const { handleToolDecision, handleAuthenticated, resumeGatedTool } = useGateActions(args);
211
- const handleStop = useCallback(() => {
212
- args.abortRef.current?.abort();
213
- }, [args]);
214
- const handleSubmit = useCallback(async () => {
215
- if (args.phase === 'streaming' || args.phase === 'gated') {
216
- await enqueuePending('queue');
217
- return;
218
- }
219
- if (args.gated)
220
- return;
221
- args.setIssues([]);
222
- await startTurnFromFields({
223
- text: args.draftText,
224
- files: [...args.pendingFiles],
225
- voice: [...args.pendingVoice],
226
- });
227
- }, [args, enqueuePending, startTurnFromFields]);
228
- /** The queued message's draft, or the composer's when it holds something to send. */
229
- const draftToSend = useCallback(async (draftSource) => {
230
- if (draftSource)
231
- return draftSource.draft;
232
- const draft = await encodeComposerDraft({
233
- text: args.draftText,
234
- pendingFiles: args.pendingFiles,
235
- pendingVoice: args.pendingVoice,
236
- });
237
- return userDraftHasPayload(draft) ? draft : undefined;
238
- }, [args]);
239
- /** The sent message leaves the queue, or the composer it came from. */
240
- const releaseDraft = useCallback((draftSource) => {
241
- if (!draftSource) {
242
- args.clearComposer();
243
- return;
244
- }
245
- const pendingId = draftSource.id;
246
- args.setPendingMessages((prev) => removeComposerPendingMessage(prev, pendingId));
247
- }, [args]);
248
- const handleSendNow = useCallback(async (draftSource) => {
249
- if (!args.iface)
250
- return;
251
- const draft = await draftToSend(draftSource);
252
- if (!draft)
253
- return;
254
- if (args.busyRef.current) {
255
- args.abortRef.current?.abort();
256
- await args.runPromiseRef.current;
257
- }
258
- args.setPendingMessages((prev) => convertSteersToFrontQueued(prev));
259
- args.allowQueueDrainRef.current = false;
260
- // why: Sent while the reply waits on gates, the message walks away from them in its own request.
261
- await startTurnFromDraft(draft, {
262
- walkAway: args.sessionRef.current.gatedTool !== null,
263
- onPosted: () => releaseDraft(draftSource),
264
- });
265
- }, [args, draftToSend, releaseDraft, startTurnFromDraft]);
266
- const handleMenuAction = useCallback((action) => {
267
- if (action === 'queue' || action === 'steer' || action === 'stash') {
268
- void enqueuePending(action);
269
- return;
270
- }
271
- if (action === 'send_now')
272
- void handleSendNow();
273
- }, [enqueuePending, handleSendNow]);
274
- return {
275
- startTurnFromFields,
276
- startTurnFromDraft,
277
- handleStop,
278
- handleSubmit,
279
- handleSendNow,
280
- handleMenuAction,
281
- handleToolDecision,
282
- handleAuthenticated,
283
- resumeGatedTool,
284
- handlePendingRestore,
285
- enqueuePending,
286
- };
287
- }