@theoremjs/react 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +728 -202
- package/client/composer-hints.js +1 -1
- package/client/decision-images.d.ts +76 -0
- package/client/decision-images.js +139 -0
- package/client/decision-transport.d.ts +26 -4
- package/client/decision-transport.js +79 -6
- package/client/embedding-input.d.ts +84 -0
- package/client/embedding-input.js +194 -0
- package/client/embedding-space.d.ts +82 -0
- package/client/embedding-space.js +389 -0
- package/client/embedding-transport.d.ts +40 -0
- package/client/embedding-transport.js +71 -0
- package/client/failure.d.ts +7 -10
- package/client/failure.js +9 -9
- package/client/host-call.d.ts +1 -1
- package/client/host-call.js +7 -2
- package/client/host-transport.d.ts +2 -0
- package/client/host-transport.js +3 -2
- package/client/index.d.ts +19 -11
- package/client/index.js +14 -10
- package/client/ink-waveform.d.ts +1 -1
- package/client/ink-waveform.js +1 -2
- package/client/live/apply-live-turn-tool-event.js +1 -1
- package/client/live/live-captions.d.ts +30 -0
- package/client/live/live-captions.js +105 -1
- package/client/live/live-mic-forward.d.ts +1 -0
- package/client/live/live-mic-forward.js +11 -2
- package/client/live/live-page-tool.d.ts +34 -0
- package/client/live/live-page-tool.js +11 -0
- package/client/live/live-state.d.ts +4 -2
- package/client/live/live-state.js +2 -0
- package/client/live/live-video.js +1 -1
- package/client/live/run-live-tool-call.d.ts +7 -2
- package/client/live/run-live-tool-call.js +48 -10
- package/client/live/use-live-caption-log.d.ts +17 -0
- package/client/live/use-live-caption-log.js +51 -0
- package/client/live-client.d.ts +52 -4
- package/client/live-client.js +250 -59
- package/client/live-messages.d.ts +51 -13
- package/client/live-messages.js +42 -9
- package/client/page-dispatch.d.ts +15 -0
- package/client/page-dispatch.js +63 -0
- package/client/run-commit.d.ts +16 -43
- package/client/run-commit.js +26 -101
- package/client/run-session.d.ts +40 -41
- package/client/run-session.js +104 -112
- package/client/schema-fields.js +27 -6
- package/client/shaped-data.js +37 -8
- package/client/source-citations.js +1 -1
- package/client/space-geometry.d.ts +39 -0
- package/client/space-geometry.js +183 -0
- package/client/state-fields.js +1 -1
- package/client/tool-result.js +30 -12
- package/client/tool-resume.d.ts +18 -1
- package/client/tool-resume.js +4 -1
- package/client/trace-feed.d.ts +1 -1
- package/client/trace-story.d.ts +2 -2
- package/client/trace-story.js +43 -19
- package/client/trace-view.d.ts +2 -2
- package/client/trace-view.js +56 -16
- package/client/transcript-groups.d.ts +28 -2
- package/client/transcript-groups.js +63 -13
- package/client/transcript-schema.d.ts +10 -0
- package/client/transcript-schema.js +120 -0
- package/client/transport.d.ts +114 -26
- package/client/transport.js +106 -32
- package/client/turn-client.d.ts +17 -8
- package/client/turn-client.js +26 -31
- package/client/voice-recorder.d.ts +1 -1
- package/client/voice-recorder.js +2 -5
- package/client/wire-line.js +5 -5
- package/client/worklets/mic-capture.js +9 -9
- package/components/InkWaveform.d.ts +12 -1
- package/components/InkWaveform.js +65 -42
- package/components/live/use-live-runner-controls.d.ts +3 -4
- package/components/live/use-live-runner-controls.js +6 -10
- package/components/live/use-live-runner-model.d.ts +10 -6
- package/components/live/use-live-runner-model.js +29 -14
- package/components/live/use-live-runner-ui.d.ts +10 -8
- package/components/live/use-live-runner-ui.js +19 -11
- package/components/live/use-live-session-client.d.ts +4 -4
- package/components/live/use-live-session-client.js +11 -8
- package/components/use-composer-voice.d.ts +4 -4
- package/components/use-composer-voice.js +8 -8
- package/components/use-editor-selection.js +4 -1
- package/components/use-voice-playback.d.ts +2 -1
- package/components/use-voice-playback.js +4 -4
- package/hooks/use-described.d.ts +5 -1
- package/hooks/use-described.js +11 -9
- package/hooks/use-embedding-index.d.ts +27 -0
- package/hooks/use-embedding-index.js +43 -0
- package/hooks/use-one-call.d.ts +31 -0
- package/hooks/use-one-call.js +76 -0
- package/hooks/use-page-tool-warnings.d.ts +6 -0
- package/hooks/use-page-tool-warnings.js +25 -0
- package/hooks/use-second-ticker.d.ts +2 -0
- package/hooks/use-second-ticker.js +13 -0
- package/hooks/use-theorem-chat-actions.d.ts +25 -21
- package/hooks/use-theorem-chat-actions.js +67 -45
- package/hooks/use-theorem-chat-state.d.ts +6 -6
- package/hooks/use-theorem-chat-state.js +13 -9
- package/hooks/use-theorem-chat.d.ts +48 -14
- package/hooks/use-theorem-chat.js +329 -141
- package/hooks/use-theorem-decision.d.ts +5 -10
- package/hooks/use-theorem-decision.js +4 -67
- package/hooks/use-theorem-embedding.d.ts +18 -0
- package/hooks/use-theorem-embedding.js +12 -0
- package/hooks/use-theorem-host.d.ts +5 -4
- package/hooks/use-theorem-host.js +26 -8
- package/hooks/use-theorem-interface.d.ts +6 -3
- package/hooks/use-theorem-interface.js +12 -22
- package/index.d.ts +6 -3
- package/index.js +5 -2
- package/live.d.ts +1 -0
- package/live.js +1 -1
- package/package.json +6 -5
- package/server/checkpoint-ownership.d.ts +6 -0
- package/server/checkpoint-ownership.js +42 -0
- package/server/conversation-store.d.ts +92 -0
- package/server/conversation-store.js +131 -0
- package/server/conversation.d.ts +193 -0
- package/server/conversation.js +552 -0
- package/server/decision-handler.d.ts +12 -4
- package/server/decision-handler.js +29 -38
- package/server/embedding-handler.d.ts +33 -0
- package/server/embedding-handler.js +45 -0
- package/server/handler.d.ts +58 -20
- package/server/handler.js +441 -238
- package/server/mod.d.ts +15 -6
- package/server/mod.js +13 -5
- package/server/page-dispatch.d.ts +22 -0
- package/server/page-dispatch.js +127 -0
- package/server/request-body.d.ts +2 -0
- package/server/request-body.js +42 -0
- package/server/request-check.d.ts +6 -1
- package/server/request-check.js +27 -12
- package/server/session-authority.d.ts +8 -0
- package/server/session-authority.js +78 -0
- package/server/session-boundary.d.ts +26 -0
- package/server/session-boundary.js +64 -0
- package/server/session-identity.d.ts +16 -0
- package/server/session-identity.js +41 -0
- package/server/session-limits.d.ts +12 -0
- package/server/session-limits.js +59 -0
- package/server/session-store.d.ts +69 -20
- package/server/session-store.js +220 -5
- package/server/single-action.d.ts +21 -0
- package/server/single-action.js +30 -0
- package/server/steer-inbox.js +1 -1
- package/server/tool-operations.d.ts +6 -0
- package/server/tool-operations.js +132 -0
- package/server/turn-input.d.ts +10 -0
- package/server/turn-input.js +29 -0
- package/styles/ink-controls.css +78 -78
- package/ui/CardWall.d.ts +10 -0
- package/ui/CardWall.js +61 -0
- package/ui/ChatComposerBar.d.ts +28 -5
- package/ui/ChatComposerBar.js +85 -30
- package/ui/ChatTranscript.d.ts +14 -1
- package/ui/ChatTranscript.js +110 -57
- package/ui/ConsoleFrame.d.ts +61 -4
- package/ui/ConsoleFrame.js +109 -9
- package/ui/DecisionAnswers.d.ts +40 -2
- package/ui/DecisionAnswers.js +166 -35
- package/ui/DecisionImages.d.ts +16 -0
- package/ui/DecisionImages.js +46 -0
- package/ui/EmbeddingDocuments.d.ts +78 -0
- package/ui/EmbeddingDocuments.js +278 -0
- package/ui/EmbeddingQuery.d.ts +27 -0
- package/ui/EmbeddingQuery.js +95 -0
- package/ui/EmbeddingResults.d.ts +41 -0
- package/ui/EmbeddingResults.js +165 -0
- package/ui/EmbeddingSpace.d.ts +26 -0
- package/ui/EmbeddingSpace.js +106 -0
- package/ui/LiveCaptionsPanel.d.ts +22 -0
- package/ui/LiveCaptionsPanel.js +27 -0
- package/ui/LiveRunner.d.ts +6 -4
- package/ui/LiveRunner.js +42 -55
- package/ui/PaneState.d.ts +33 -0
- package/ui/PaneState.js +50 -0
- package/ui/SchemaFields.d.ts +1 -1
- package/ui/SchemaFields.js +54 -26
- package/ui/ShapedData.js +73 -28
- package/ui/SidePanel.d.ts +74 -5
- package/ui/SidePanel.js +82 -9
- package/ui/StateFields.d.ts +10 -2
- package/ui/StateFields.js +56 -13
- package/ui/TheoremChat.d.ts +65 -14
- package/ui/TheoremChat.js +66 -78
- package/ui/TheoremDecision.d.ts +12 -20
- package/ui/TheoremDecision.js +78 -35
- package/ui/TheoremEmbedding.d.ts +27 -0
- package/ui/TheoremEmbedding.js +122 -0
- package/ui/TheoremHost.d.ts +22 -5
- package/ui/TheoremHost.js +127 -37
- package/ui/ToolGateCard.d.ts +5 -1
- package/ui/ToolGateCard.js +30 -23
- package/ui/ToolResult.js +12 -10
- package/ui/ToolResultCharts.js +29 -12
- package/ui/TraceCharts.d.ts +2 -2
- package/ui/TraceCharts.js +48 -17
- package/ui/TraceGuardrails.d.ts +3 -2
- package/ui/TraceGuardrails.js +24 -6
- package/ui/TraceInspector.d.ts +11 -1
- package/ui/TraceInspector.js +45 -28
- package/ui/TraceInspectorPanel.d.ts +31 -9
- package/ui/TraceInspectorPanel.js +77 -39
- package/ui/TraceOverview.d.ts +7 -3
- package/ui/TraceOverview.js +44 -14
- package/ui/TraceSpanDetail.js +24 -22
- package/ui/TraceStory.js +9 -8
- package/ui/TraceValues.js +19 -9
- package/ui/TraceWaterfall.js +12 -9
- package/ui/VoiceNote.js +4 -4
- package/ui/approval-heading.d.ts +4 -0
- package/ui/approval-heading.js +9 -0
- package/ui/arrive.d.ts +18 -0
- package/ui/arrive.js +46 -0
- package/ui/built/theme.css +86 -68
- package/ui/built/theme.css.d.ts +9 -0
- package/ui/built/theorem.d.ts +3 -3
- package/ui/built/theorem.js +34 -5
- package/ui/built/theorem.variants.d.ts +8 -2
- package/ui/code-language.d.ts +12 -0
- package/ui/code-language.js +50 -0
- package/ui/disclosure-motion.js +53 -9
- package/ui/embedding-space-gl.d.ts +37 -0
- package/ui/embedding-space-gl.js +663 -0
- package/ui/embedding-space-view.d.ts +8 -0
- package/ui/embedding-space-view.js +8 -0
- package/ui/glide.d.ts +7 -0
- package/ui/glide.js +44 -0
- package/ui/icons.js +2 -1
- package/ui/index.d.ts +20 -9
- package/ui/index.js +20 -10
- package/ui/labels-provider.js +5 -2
- package/ui/labels.d.ts +374 -85
- package/ui/labels.js +1028 -188
- package/ui/markdown-plugins.d.ts +9 -0
- package/ui/markdown-plugins.js +13 -0
- package/ui/mcp-icon.d.ts +6 -0
- package/ui/mcp-icon.js +8 -0
- package/ui/row-keys.d.ts +19 -0
- package/ui/row-keys.js +33 -0
- package/ui/theme.d.ts +1 -1
- package/ui/theme.js +2 -2
- package/ui/theorem-theme.js +32 -10
- package/ui/trace-palette.d.ts +2 -16
- package/ui/trace-palette.js +2 -16
- package/ui/trace-patterns.d.ts +11 -3
- package/ui/trace-patterns.js +16 -3
- package/ui/transcript-copy-text.js +1 -0
- package/ui/wall-columns.d.ts +8 -0
- package/ui/wall-columns.js +19 -0
- package/client/walk-away.d.ts +0 -34
- package/client/walk-away.js +0 -48
- package/server/walk-away.d.ts +0 -22
- package/server/walk-away.js +0 -41
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useCallback, useRef } from 'react';
|
|
2
1
|
import { TheoremError } from '@theoremjs/agents';
|
|
3
|
-
import {
|
|
2
|
+
import { useCallback, useRef, } from 'react';
|
|
3
|
+
import { applyLiveTranscript } from "../../client/live/live-captions.js";
|
|
4
4
|
import { runLiveToolCall } from "../../client/live/run-live-tool-call.js";
|
|
5
5
|
import { LiveSessionClient } from "../../client/live-client.js";
|
|
6
6
|
function onLiveStatusChange(next, bindings) {
|
|
@@ -23,25 +23,26 @@ function onLiveStatusChange(next, bindings) {
|
|
|
23
23
|
bindings.setIsVideoOn(false);
|
|
24
24
|
}
|
|
25
25
|
function onLiveTranscript(text, isUser, meta, bindings) {
|
|
26
|
-
|
|
27
|
-
bindings.
|
|
28
|
-
bindings.setCaptions(next);
|
|
26
|
+
// why: A thought or a tool call in the same batch is already queued; each folds onto the last.
|
|
27
|
+
bindings.setCaptions((prev) => applyLiveTranscript(prev, text, isUser, meta?.interim));
|
|
29
28
|
}
|
|
30
29
|
async function onLiveToolCall(name, toolArgs, meta, bindings, clientRef) {
|
|
31
30
|
bindings.setActiveTool(name);
|
|
32
31
|
bindings.clearFailure();
|
|
33
32
|
const client = clientRef.current;
|
|
34
33
|
if (!client) {
|
|
35
|
-
//
|
|
34
|
+
// labels-exempt: internal diagnostic; the user reads error.request
|
|
36
35
|
bindings.reportFailure(new TheoremError('request', 'live tool call with no session client'));
|
|
37
36
|
return;
|
|
38
37
|
}
|
|
39
38
|
try {
|
|
40
39
|
await runLiveToolCall({
|
|
40
|
+
pageTools: bindings.pageTools,
|
|
41
41
|
executeToolOnRelay: (call) => client.executeToolOnRelay(call),
|
|
42
42
|
name,
|
|
43
43
|
toolArgs,
|
|
44
44
|
callId: meta.callId,
|
|
45
|
+
operationId: meta.operationId,
|
|
45
46
|
sessionPermissions: bindings.sessionPermissionsRef.current,
|
|
46
47
|
setSessionPermissions: (next) => {
|
|
47
48
|
bindings.sessionPermissionsRef.current = next;
|
|
@@ -69,6 +70,8 @@ export function useLiveSessionClient(bindings) {
|
|
|
69
70
|
const b = bindingsRef.current;
|
|
70
71
|
const client = new LiveSessionClient({
|
|
71
72
|
...connection,
|
|
73
|
+
slots: b.slots,
|
|
74
|
+
context: b.context,
|
|
72
75
|
voiceIngress: b.voiceAvailable,
|
|
73
76
|
onConnectPhase: (phase) => {
|
|
74
77
|
b.setConnectPhase(phase);
|
|
@@ -80,10 +83,10 @@ export function useLiveSessionClient(bindings) {
|
|
|
80
83
|
onLiveTranscript(text, isUser, meta, bindingsRef.current);
|
|
81
84
|
},
|
|
82
85
|
onTurnEvent: (event) => {
|
|
83
|
-
// An `unsupported` event is the host's to read; Theorem's UI shows nothing for it.
|
|
86
|
+
// why: An `unsupported` event is the host's to read; Theorem's UI shows nothing for it.
|
|
84
87
|
if (event.type === 'unsupported')
|
|
85
88
|
return;
|
|
86
|
-
// A `malformed` one was left out and the call goes on; the user reads that part was skipped.
|
|
89
|
+
// why: A `malformed` one was left out and the call goes on; the user reads that part was skipped.
|
|
87
90
|
if (event.type === 'malformed') {
|
|
88
91
|
bindingsRef.current.reportFailure(event.error);
|
|
89
92
|
return;
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type LabelOverrides } from '@theoremjs/agents';
|
|
2
2
|
import type { ProfileInputsInterface } from '@theoremjs/agents/interface';
|
|
3
3
|
import { type VoiceRecorderFailureCode } from '../client/voice-recorder.ts';
|
|
4
4
|
/** A voice note that could not be recorded or staged. */
|
|
5
5
|
export type VoiceFailure = {
|
|
6
6
|
/** A recorder failure, or `too_many_files` when the message is already full. */
|
|
7
7
|
code: VoiceRecorderFailureCode | 'too_many_files';
|
|
8
|
-
/** What the user reads: the profile
|
|
8
|
+
/** What the user reads: the profile labels' `voice.<code>` (or the file-limit line). */
|
|
9
9
|
error: string;
|
|
10
10
|
/** Raw detail for the builder; never shown to the user. */
|
|
11
11
|
errorInternal?: string;
|
|
12
12
|
};
|
|
13
13
|
export declare function useComposerVoice(args: {
|
|
14
14
|
inputs: ProfileInputsInterface;
|
|
15
|
-
/** The interface's `
|
|
16
|
-
|
|
15
|
+
/** The interface's `labels`: the profile's wording for voice failures. */
|
|
16
|
+
labels: LabelOverrides;
|
|
17
17
|
pendingFiles: readonly File[];
|
|
18
18
|
onVoiceStaged?: (file: File) => void;
|
|
19
19
|
onVoiceClear?: () => void;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import { attachmentIssueText, describeError, labelText, } from '@theoremjs/agents';
|
|
1
2
|
import { useCallback, useRef, useState } from 'react';
|
|
2
|
-
import { attachmentIssueText, describeError, lexiconText, } from '@theoremjs/agents';
|
|
3
3
|
import { canStageVoice } from "../client/composer-attachments.js";
|
|
4
4
|
import { ComposerVoiceRecorder, isVoiceRecorderFailure, } from "../client/voice-recorder.js";
|
|
5
|
-
function voiceFailure(err, fallback,
|
|
5
|
+
function voiceFailure(err, fallback, labels) {
|
|
6
6
|
const code = isVoiceRecorderFailure(err) ? err.code : fallback;
|
|
7
7
|
const internal = describeError(err);
|
|
8
8
|
return {
|
|
9
9
|
code,
|
|
10
|
-
error:
|
|
10
|
+
error: labelText(`voice.${code}`, {}, labels),
|
|
11
11
|
...(internal && internal !== code ? { errorInternal: internal } : {}),
|
|
12
12
|
};
|
|
13
13
|
}
|
|
@@ -32,7 +32,7 @@ export function useComposerVoice(args) {
|
|
|
32
32
|
if (!canStageVoice({ fileCount: args.pendingFiles.length, maxFiles })) {
|
|
33
33
|
setFailure({
|
|
34
34
|
code: 'too_many_files',
|
|
35
|
-
error: attachmentIssueText({ code: 'too_many_files', params: { maxFiles } }, args.
|
|
35
|
+
error: attachmentIssueText({ code: 'too_many_files', params: { maxFiles } }, args.labels),
|
|
36
36
|
});
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
@@ -43,9 +43,9 @@ export function useComposerVoice(args) {
|
|
|
43
43
|
}
|
|
44
44
|
catch (err) {
|
|
45
45
|
setRecording(false);
|
|
46
|
-
setFailure(voiceFailure(err, 'unavailable', args.
|
|
46
|
+
setFailure(voiceFailure(err, 'unavailable', args.labels));
|
|
47
47
|
}
|
|
48
|
-
}, [args.inputs.maxFiles, args.
|
|
48
|
+
}, [args.inputs.maxFiles, args.labels, args.pendingFiles.length, ensureRecorder]);
|
|
49
49
|
const stopRecording = useCallback(async () => {
|
|
50
50
|
setFailure(null);
|
|
51
51
|
try {
|
|
@@ -57,9 +57,9 @@ export function useComposerVoice(args) {
|
|
|
57
57
|
catch (err) {
|
|
58
58
|
setRecording(false);
|
|
59
59
|
setInputLevel(0);
|
|
60
|
-
setFailure(voiceFailure(err, 'failed', args.
|
|
60
|
+
setFailure(voiceFailure(err, 'failed', args.labels));
|
|
61
61
|
}
|
|
62
|
-
}, [args.
|
|
62
|
+
}, [args.labels, ensureRecorder]);
|
|
63
63
|
const toggleRecording = useCallback(async () => {
|
|
64
64
|
if (recording) {
|
|
65
65
|
await stopRecording();
|
|
@@ -6,7 +6,10 @@ export function useEditorSelection(ref) {
|
|
|
6
6
|
const read = () => {
|
|
7
7
|
const root = ref.current;
|
|
8
8
|
const selection = document.getSelection();
|
|
9
|
-
const inside = root &&
|
|
9
|
+
const inside = root &&
|
|
10
|
+
selection &&
|
|
11
|
+
selection.rangeCount > 0 &&
|
|
12
|
+
root.contains(selection.getRangeAt(0).commonAncestorContainer);
|
|
10
13
|
setSelected(inside ? selection.toString() : '');
|
|
11
14
|
};
|
|
12
15
|
document.addEventListener('selectionchange', read);
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import type { InkWaveLevels } from './InkWaveform.tsx';
|
|
1
2
|
/**
|
|
2
3
|
* Click-to-play state for a voice note: an `<audio>` ref, whether it plays,
|
|
3
4
|
* and a gentle output level that drives the ink waveform while it does.
|
|
4
5
|
*/
|
|
5
6
|
export declare function useVoicePlayback(): {
|
|
6
7
|
playing: boolean;
|
|
7
|
-
|
|
8
|
+
levelsRef: import("react").RefObject<InkWaveLevels>;
|
|
8
9
|
toggle: () => Promise<void>;
|
|
9
10
|
audioProps: {
|
|
10
11
|
ref: import("react").RefObject<HTMLAudioElement | null>;
|
|
@@ -7,19 +7,19 @@ export function useVoicePlayback() {
|
|
|
7
7
|
const audioRef = useRef(null);
|
|
8
8
|
const [playing, setPlaying] = useState(false);
|
|
9
9
|
const playingRef = useRef(false);
|
|
10
|
-
const
|
|
10
|
+
const levelsRef = useRef({ input: 0, output: 0 });
|
|
11
11
|
const rafIdRef = useRef(0);
|
|
12
12
|
function stopMeter() {
|
|
13
13
|
cancelAnimationFrame(rafIdRef.current);
|
|
14
14
|
rafIdRef.current = 0;
|
|
15
|
-
|
|
15
|
+
levelsRef.current.output = 0;
|
|
16
16
|
}
|
|
17
17
|
function startMeter() {
|
|
18
18
|
stopMeter();
|
|
19
19
|
const tick = (time) => {
|
|
20
20
|
if (!playingRef.current)
|
|
21
21
|
return;
|
|
22
|
-
|
|
22
|
+
levelsRef.current.output = 0.28 + 0.22 * (0.5 + 0.5 * Math.sin(time * 0.008));
|
|
23
23
|
rafIdRef.current = requestAnimationFrame(tick);
|
|
24
24
|
};
|
|
25
25
|
rafIdRef.current = requestAnimationFrame(tick);
|
|
@@ -64,5 +64,5 @@ export function useVoicePlayback() {
|
|
|
64
64
|
audioRef.current.currentTime = 0;
|
|
65
65
|
},
|
|
66
66
|
};
|
|
67
|
-
return { playing,
|
|
67
|
+
return { playing, levelsRef, toggle, audioProps };
|
|
68
68
|
}
|
package/hooks/use-described.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { type ClientFailure } from '../client/failure.ts';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* What a transport describes, read once per transport; a failure when it can't be read. A new
|
|
4
|
+
* transport keeps the last description up until it describes itself, so swapping one in (a
|
|
5
|
+
* recompiled profile) changes the page in place instead of blanking it to a spinner.
|
|
6
|
+
*/
|
|
3
7
|
export declare function useDescribed<T>(transport: {
|
|
4
8
|
describe: (signal?: AbortSignal) => Promise<T>;
|
|
5
9
|
}): {
|
package/hooks/use-described.js
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
import { clientFailure } from "../client/failure.js";
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* What a transport describes, read once per transport; a failure when it can't be read. A new
|
|
5
|
+
* transport keeps the last description up until it describes itself, so swapping one in (a
|
|
6
|
+
* recompiled profile) changes the page in place instead of blanking it to a spinner.
|
|
7
|
+
*/
|
|
4
8
|
export function useDescribed(transport) {
|
|
5
|
-
const [description, setDescription] = useState(
|
|
9
|
+
const [description, setDescription] = useState(NOT_DESCRIBED);
|
|
6
10
|
useEffect(() => {
|
|
7
11
|
const controller = new AbortController();
|
|
8
12
|
transport.describe(controller.signal).then((described) => {
|
|
9
|
-
if (controller.signal.aborted)
|
|
10
|
-
|
|
11
|
-
setDescription({ transport, iface: described, describeFailure: null });
|
|
13
|
+
if (!controller.signal.aborted)
|
|
14
|
+
setDescription({ iface: described, describeFailure: null });
|
|
12
15
|
}, (err) => {
|
|
13
16
|
if (!controller.signal.aborted)
|
|
14
|
-
setDescription({
|
|
17
|
+
setDescription({ iface: null, describeFailure: clientFailure(err) });
|
|
15
18
|
});
|
|
16
19
|
return () => {
|
|
17
20
|
controller.abort();
|
|
18
21
|
};
|
|
19
22
|
}, [transport]);
|
|
20
|
-
return description
|
|
21
|
-
? { iface: description.iface, describeFailure: description.describeFailure }
|
|
22
|
-
: { iface: null, describeFailure: null };
|
|
23
|
+
return description;
|
|
23
24
|
}
|
|
25
|
+
const NOT_DESCRIBED = { iface: null, describeFailure: null };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { EmbeddingInput, EmbeddingUsage } from '@theoremjs/agents';
|
|
2
|
+
import type { EmbeddingInterface, EmbeddingTransport } from '../client/embedding-transport.ts';
|
|
3
|
+
import { type OneCallState } from './use-one-call.ts';
|
|
4
|
+
/** Documents embedded for search: each one's vector, in the order given. */
|
|
5
|
+
export type EmbeddingIndex = {
|
|
6
|
+
inputs: readonly EmbeddingInput[];
|
|
7
|
+
vectors: readonly (readonly number[])[];
|
|
8
|
+
model: string;
|
|
9
|
+
usage?: EmbeddingUsage;
|
|
10
|
+
/** How many requests it took. */
|
|
11
|
+
requests: number;
|
|
12
|
+
};
|
|
13
|
+
/** How far an indexing has come, in requests. */
|
|
14
|
+
export type IndexProgress = {
|
|
15
|
+
done: number;
|
|
16
|
+
total: number;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Documents embedded for search with `task: 'document'`, as many requests as
|
|
20
|
+
* the profile's limits need, one after another. A new index aborts the one
|
|
21
|
+
* still running; a request that fails stops it.
|
|
22
|
+
*/
|
|
23
|
+
export declare function useEmbeddingIndex(transport: EmbeddingTransport): OneCallState<'indexing', EmbeddingIndex> & {
|
|
24
|
+
progress: IndexProgress | null;
|
|
25
|
+
index: (inputs: readonly EmbeddingInput[], rules: EmbeddingInterface['inputs']) => Promise<void>;
|
|
26
|
+
cancel: () => void;
|
|
27
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { addUsage, embeddingBatches } from "../client/embedding-input.js";
|
|
3
|
+
import { useOneCall } from "./use-one-call.js";
|
|
4
|
+
async function indexWith(transport, signal, inputs, rules, onProgress) {
|
|
5
|
+
const batches = embeddingBatches(inputs, rules);
|
|
6
|
+
const vectors = [];
|
|
7
|
+
let model = '';
|
|
8
|
+
let usage;
|
|
9
|
+
onProgress({ done: 0, total: batches.length });
|
|
10
|
+
for (const [at, batch] of batches.entries()) {
|
|
11
|
+
const reply = await transport.embed(batch, { task: 'document' }, signal);
|
|
12
|
+
// why: Each request's records go to the trace as it lands, so a long index shows its calls one by one.
|
|
13
|
+
for (const record of reply.traces ?? [])
|
|
14
|
+
transport.traces?.push(record);
|
|
15
|
+
vectors.push(...reply.result.embeddings.map((vector) => vector.values));
|
|
16
|
+
model = reply.result.model;
|
|
17
|
+
usage = addUsage(usage, reply.result.usage);
|
|
18
|
+
onProgress({ done: at + 1, total: batches.length });
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
result: {
|
|
22
|
+
inputs,
|
|
23
|
+
vectors,
|
|
24
|
+
model,
|
|
25
|
+
...(usage ? { usage } : {}),
|
|
26
|
+
requests: batches.length,
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Documents embedded for search with `task: 'document'`, as many requests as
|
|
32
|
+
* the profile's limits need, one after another. A new index aborts the one
|
|
33
|
+
* still running; a request that fails stops it.
|
|
34
|
+
*/
|
|
35
|
+
export function useEmbeddingIndex(transport) {
|
|
36
|
+
const [progress, setProgress] = useState(null);
|
|
37
|
+
const { call, ...run } = useOneCall(transport, 'indexing', indexWith);
|
|
38
|
+
return {
|
|
39
|
+
...run,
|
|
40
|
+
progress: run.status === 'indexing' ? progress : null,
|
|
41
|
+
index: (inputs, rules) => call(inputs, rules, setProgress),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { TraceRecord } from '@theoremjs/agents';
|
|
2
|
+
import { type ClientFailure } from '../client/failure.ts';
|
|
3
|
+
import type { TraceFeed } from '../client/trace-feed.ts';
|
|
4
|
+
/** A profile that answers in one call: a decision or an embedding. */
|
|
5
|
+
export type OneCallTransport = {
|
|
6
|
+
traces?: TraceFeed;
|
|
7
|
+
};
|
|
8
|
+
/** The last call's state: its result is kept while the next one runs and when it fails. */
|
|
9
|
+
export type OneCallState<Busy extends string, Result> = {
|
|
10
|
+
status: 'idle' | Busy | 'done' | 'error';
|
|
11
|
+
result: Result | null;
|
|
12
|
+
/** How long the last call took, round trip, in milliseconds. */
|
|
13
|
+
elapsedMs: number | null;
|
|
14
|
+
/** When the call now running began, as `Date.now()`; null while none runs. */
|
|
15
|
+
startedAt: number | null;
|
|
16
|
+
failure: ClientFailure | null;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* One call at a time to `transport`, through `send`: a new call aborts the one
|
|
20
|
+
* still running, and the last result stays on screen while the next one runs.
|
|
21
|
+
* A new transport (a recompiled profile) keeps the result and any call still
|
|
22
|
+
* running, as a chat keeps its transcript; give the component a new `key` to
|
|
23
|
+
* start over. `send` must not change between renders.
|
|
24
|
+
*/
|
|
25
|
+
export declare function useOneCall<T extends OneCallTransport, Busy extends string, Args extends unknown[], Result>(transport: T, busy: Busy, send: (transport: T, signal: AbortSignal, ...args: Args) => Promise<{
|
|
26
|
+
result: Result;
|
|
27
|
+
traces?: readonly TraceRecord[];
|
|
28
|
+
}>): OneCallState<Busy, Result> & {
|
|
29
|
+
call: (...args: Args) => Promise<void>;
|
|
30
|
+
cancel: () => void;
|
|
31
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { failedTraces } from "../client/decision-transport.js";
|
|
3
|
+
import { clientFailure } from "../client/failure.js";
|
|
4
|
+
const IDLE = {
|
|
5
|
+
status: 'idle',
|
|
6
|
+
result: null,
|
|
7
|
+
elapsedMs: null,
|
|
8
|
+
startedAt: null,
|
|
9
|
+
failure: null,
|
|
10
|
+
};
|
|
11
|
+
/** Hands a call's trace records to the transport's feed, when it has one. */
|
|
12
|
+
function feed(transport, records) {
|
|
13
|
+
for (const record of records ?? [])
|
|
14
|
+
transport.traces?.push(record);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* One call at a time to `transport`, through `send`: a new call aborts the one
|
|
18
|
+
* still running, and the last result stays on screen while the next one runs.
|
|
19
|
+
* A new transport (a recompiled profile) keeps the result and any call still
|
|
20
|
+
* running, as a chat keeps its transcript; give the component a new `key` to
|
|
21
|
+
* start over. `send` must not change between renders.
|
|
22
|
+
*/
|
|
23
|
+
export function useOneCall(transport, busy, send) {
|
|
24
|
+
const [run, setRun] = useState(IDLE);
|
|
25
|
+
const running = useRef(null);
|
|
26
|
+
useEffect(() => () => running.current?.abort(), []);
|
|
27
|
+
const cancel = useCallback(() => {
|
|
28
|
+
running.current?.abort();
|
|
29
|
+
running.current = null;
|
|
30
|
+
setRun((previous) => previous.status === busy
|
|
31
|
+
? { ...previous, status: previous.result ? 'done' : 'idle', startedAt: null }
|
|
32
|
+
: previous);
|
|
33
|
+
}, [busy]);
|
|
34
|
+
const call = useCallback(async (...args) => {
|
|
35
|
+
running.current?.abort();
|
|
36
|
+
const controller = new AbortController();
|
|
37
|
+
running.current = controller;
|
|
38
|
+
setRun((previous) => ({
|
|
39
|
+
status: busy,
|
|
40
|
+
result: previous.result,
|
|
41
|
+
elapsedMs: previous.elapsedMs,
|
|
42
|
+
startedAt: Date.now(),
|
|
43
|
+
failure: null,
|
|
44
|
+
}));
|
|
45
|
+
const started = performance.now();
|
|
46
|
+
try {
|
|
47
|
+
const reply = await send(transport, controller.signal, ...args);
|
|
48
|
+
if (controller.signal.aborted)
|
|
49
|
+
return;
|
|
50
|
+
feed(transport, reply.traces);
|
|
51
|
+
setRun({
|
|
52
|
+
status: 'done',
|
|
53
|
+
result: reply.result,
|
|
54
|
+
elapsedMs: performance.now() - started,
|
|
55
|
+
startedAt: null,
|
|
56
|
+
failure: null,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
if (controller.signal.aborted)
|
|
61
|
+
return;
|
|
62
|
+
feed(transport, failedTraces(err));
|
|
63
|
+
setRun((previous) => ({
|
|
64
|
+
...previous,
|
|
65
|
+
status: 'error',
|
|
66
|
+
startedAt: null,
|
|
67
|
+
failure: clientFailure(err),
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
if (running.current === controller)
|
|
72
|
+
running.current = null;
|
|
73
|
+
}
|
|
74
|
+
}, [transport, busy, send]);
|
|
75
|
+
return { ...run, call, cancel };
|
|
76
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type PageTools } from '../client/live/live-page-tool.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Says in the console, once, where the page's tools and the profile's disagree. A wrong name is
|
|
4
|
+
* the builder's to fix, so it is never said to the visitor.
|
|
5
|
+
*/
|
|
6
|
+
export declare function usePageToolWarnings(declared: readonly string[] | undefined, pageTools: PageTools | undefined): void;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { pageToolMismatch } from "../client/live/live-page-tool.js";
|
|
3
|
+
/**
|
|
4
|
+
* Says in the console, once, where the page's tools and the profile's disagree. A wrong name is
|
|
5
|
+
* the builder's to fix, so it is never said to the visitor.
|
|
6
|
+
*/
|
|
7
|
+
export function usePageToolWarnings(declared, pageTools) {
|
|
8
|
+
const handled = Object.keys(pageTools ?? {})
|
|
9
|
+
.sort()
|
|
10
|
+
.join('\n');
|
|
11
|
+
const named = declared?.join('\n');
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (named === undefined)
|
|
14
|
+
return;
|
|
15
|
+
const { unanswered, unused } = pageToolMismatch(named ? named.split('\n') : [], handled ? handled.split('\n') : []);
|
|
16
|
+
for (const name of unanswered) {
|
|
17
|
+
// labels-exempt: builder diagnostic
|
|
18
|
+
console.warn(`Theorem: page tool '${name}' has no handler in pageTools; the agent gets no answer.`);
|
|
19
|
+
}
|
|
20
|
+
for (const name of unused) {
|
|
21
|
+
// labels-exempt: builder diagnostic
|
|
22
|
+
console.warn(`Theorem: pageTools has '${name}', which the profile does not declare answeredBy: 'page'.`);
|
|
23
|
+
}
|
|
24
|
+
}, [named, handled]);
|
|
25
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
/** `Date.now()`, refreshed every second while `active`. */
|
|
3
|
+
export function useSecondTicker(active) {
|
|
4
|
+
const [now, setNow] = useState(() => Date.now());
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
if (!active)
|
|
7
|
+
return;
|
|
8
|
+
setNow(Date.now());
|
|
9
|
+
const timer = globalThis.setInterval(() => setNow(Date.now()), 1_000);
|
|
10
|
+
return () => globalThis.clearInterval(timer);
|
|
11
|
+
}, [active]);
|
|
12
|
+
return now;
|
|
13
|
+
}
|
|
@@ -1,22 +1,15 @@
|
|
|
1
|
-
import { type MutableRefObject } from 'react';
|
|
2
1
|
import type { AttachmentValidationIssue, ComposerMenuAction, ComposerPendingMessage, ComposerProfileInterface, ComposerRunPhase, InterfaceTurnSession, TranscriptBlock } from '@theoremjs/agents/interface';
|
|
3
|
-
import { type
|
|
4
|
-
import { type ClientFailure
|
|
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';
|
|
5
6
|
import type { TheoremTransport } from '../client/transport.ts';
|
|
6
7
|
import type { MessageDelivery } from './use-theorem-chat-state.ts';
|
|
7
|
-
export type RunTurnStream = (run: (view: StreamView
|
|
8
|
-
/** The
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
ok: true;
|
|
13
|
-
session: InterfaceTurnSession;
|
|
14
|
-
userBlocks?: TranscriptBlock[];
|
|
15
|
-
assistantBlocks: TranscriptBlock[];
|
|
16
|
-
} | TurnFailure>, options?: {
|
|
17
|
-
userBlocksAlreadyApplied?: boolean;
|
|
18
|
-
/** The run's message walks away from the gates the reply waits on, ending that reply. */
|
|
19
|
-
walksAway?: boolean;
|
|
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
|
+
};
|
|
20
13
|
}) => Promise<void>;
|
|
21
14
|
export type TheoremChatActionArgs = {
|
|
22
15
|
iface: ComposerProfileInterface | null;
|
|
@@ -26,6 +19,14 @@ export type TheoremChatActionArgs = {
|
|
|
26
19
|
draftText: string;
|
|
27
20
|
pendingFiles: File[];
|
|
28
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>;
|
|
29
30
|
clearComposer: () => void;
|
|
30
31
|
runTurnStream: RunTurnStream;
|
|
31
32
|
sessionRef: MutableRefObject<InterfaceTurnSession>;
|
|
@@ -52,12 +53,13 @@ export type TheoremChatActionArgs = {
|
|
|
52
53
|
} | null) => void;
|
|
53
54
|
pendingRef: MutableRefObject<ComposerPendingMessage[]>;
|
|
54
55
|
};
|
|
56
|
+
type ComposerFields = {
|
|
57
|
+
text: string;
|
|
58
|
+
files: File[];
|
|
59
|
+
voice: File[];
|
|
60
|
+
};
|
|
55
61
|
export declare function useTheoremChatActions(args: TheoremChatActionArgs): {
|
|
56
|
-
startTurnFromFields: (fields:
|
|
57
|
-
text: string;
|
|
58
|
-
files: File[];
|
|
59
|
-
voice: File[];
|
|
60
|
-
}) => Promise<void>;
|
|
62
|
+
startTurnFromFields: (fields: ComposerFields) => Promise<void>;
|
|
61
63
|
startTurnFromDraft: (draft: ComposerPendingMessage["draft"], options?: {
|
|
62
64
|
/** Send while the reply waits on gates: the message walks away from them. */
|
|
63
65
|
walkAway?: boolean;
|
|
@@ -70,6 +72,8 @@ export declare function useTheoremChatActions(args: TheoremChatActionArgs): {
|
|
|
70
72
|
handleMenuAction: (action: ComposerMenuAction) => void;
|
|
71
73
|
handleToolDecision: (_index: number, action: ToolDecisionAction) => Promise<void>;
|
|
72
74
|
handleAuthenticated: (_index: number, secret?: string) => Promise<void>;
|
|
75
|
+
resumeGatedTool: (resolution: ToolGateResolution) => Promise<void>;
|
|
73
76
|
handlePendingRestore: (id: string) => Promise<void>;
|
|
74
77
|
enqueuePending: (kind: "queue" | "steer" | "stash") => Promise<void>;
|
|
75
78
|
};
|
|
79
|
+
export {};
|