@theoremjs/react 0.0.0-stage → 0.3.0
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/LICENSE +21 -0
- package/README.md +246 -2
- package/client/attachment-hover-preview.d.ts +11 -0
- package/client/attachment-hover-preview.js +36 -0
- package/client/audio-level.d.ts +5 -0
- package/client/audio-level.js +24 -0
- package/client/composer-attachments.d.ts +30 -0
- package/client/composer-attachments.js +33 -0
- package/client/composer-drawer.d.ts +19 -0
- package/client/composer-drawer.js +15 -0
- package/client/composer-hints.d.ts +25 -0
- package/client/composer-hints.js +20 -0
- package/client/composer-primary.d.ts +16 -0
- package/client/composer-primary.js +25 -0
- package/client/decision-transport.d.ts +48 -0
- package/client/decision-transport.js +102 -0
- package/client/decode-composer-draft.d.ts +11 -0
- package/client/decode-composer-draft.js +12 -0
- package/client/encode-composer-draft.d.ts +9 -0
- package/client/encode-composer-draft.js +34 -0
- package/client/encode-files.d.ts +18 -0
- package/client/encode-files.js +54 -0
- package/client/failure.d.ts +32 -0
- package/client/failure.js +34 -0
- package/client/generation-selection.d.ts +15 -0
- package/client/generation-selection.js +24 -0
- package/client/host-call.d.ts +51 -0
- package/client/host-call.js +42 -0
- package/client/host-transport.d.ts +68 -0
- package/client/host-transport.js +74 -0
- package/client/image-output.d.ts +6 -0
- package/client/image-output.js +13 -0
- package/client/index.d.ts +20 -0
- package/client/index.js +18 -0
- package/client/ink-waveform.d.ts +17 -0
- package/client/ink-waveform.js +69 -0
- package/client/live/apply-live-turn-tool-event.d.ts +14 -0
- package/client/live/apply-live-turn-tool-event.js +39 -0
- package/client/live/live-captions.d.ts +23 -0
- package/client/live/live-captions.js +70 -0
- package/client/live/live-mic-forward.d.ts +29 -0
- package/client/live/live-mic-forward.js +37 -0
- package/client/live/live-state.d.ts +11 -0
- package/client/live/live-state.js +20 -0
- package/client/live/live-tool.d.ts +11 -0
- package/client/live/live-tool.js +1 -0
- package/client/live/live-video.d.ts +10 -0
- package/client/live/live-video.js +71 -0
- package/client/live/run-live-tool-call.d.ts +17 -0
- package/client/live/run-live-tool-call.js +32 -0
- package/client/live-client.d.ts +141 -0
- package/client/live-client.js +638 -0
- package/client/live-errors.d.ts +2 -0
- package/client/live-errors.js +9 -0
- package/client/live-messages.d.ts +110 -0
- package/client/live-messages.js +67 -0
- package/client/mic-capture.d.ts +4 -0
- package/client/mic-capture.js +6 -0
- package/client/oauth-popup.d.ts +26 -0
- package/client/oauth-popup.js +30 -0
- package/client/pcm-downsample.d.ts +4 -0
- package/client/pcm-downsample.js +45 -0
- package/client/run-commit.d.ts +61 -0
- package/client/run-commit.js +124 -0
- package/client/run-session.d.ts +80 -0
- package/client/run-session.js +215 -0
- package/client/schema-fields.d.ts +63 -0
- package/client/schema-fields.js +178 -0
- package/client/shaped-data.d.ts +118 -0
- package/client/shaped-data.js +314 -0
- package/client/source-citations.d.ts +13 -0
- package/client/source-citations.js +56 -0
- package/client/state-fields.d.ts +16 -0
- package/client/state-fields.js +44 -0
- package/client/tool-result.d.ts +78 -0
- package/client/tool-result.js +275 -0
- package/client/tool-resume.d.ts +33 -0
- package/client/tool-resume.js +11 -0
- package/client/trace-feed.d.ts +13 -0
- package/client/trace-feed.js +18 -0
- package/client/trace-story.d.ts +163 -0
- package/client/trace-story.js +416 -0
- package/client/trace-view.d.ts +130 -0
- package/client/trace-view.js +293 -0
- package/client/transcript-groups.d.ts +114 -0
- package/client/transcript-groups.js +167 -0
- package/client/transcript-scroll.d.ts +7 -0
- package/client/transcript-scroll.js +6 -0
- package/client/transport.d.ts +176 -0
- package/client/transport.js +256 -0
- package/client/turn-client.d.ts +35 -0
- package/client/turn-client.js +93 -0
- package/client/voice-label.d.ts +2 -0
- package/client/voice-label.js +15 -0
- package/client/voice-recorder.d.ts +27 -0
- package/client/voice-recorder.js +165 -0
- package/client/walk-away.d.ts +34 -0
- package/client/walk-away.js +48 -0
- package/client/wire-line.d.ts +56 -0
- package/client/wire-line.js +90 -0
- package/client/worklets/mic-capture.js +16 -0
- package/components/InkWaveform.d.ts +10 -0
- package/components/InkWaveform.js +108 -0
- package/components/live/use-live-runner-controls.d.ts +48 -0
- package/components/live/use-live-runner-controls.js +100 -0
- package/components/live/use-live-runner-model.d.ts +45 -0
- package/components/live/use-live-runner-model.js +175 -0
- package/components/live/use-live-runner-ui.d.ts +65 -0
- package/components/live/use-live-runner-ui.js +159 -0
- package/components/live/use-live-session-client.d.ts +41 -0
- package/components/live/use-live-session-client.js +123 -0
- package/components/use-composer-voice.d.ts +28 -0
- package/components/use-composer-voice.js +90 -0
- package/components/use-editor-selection.d.ts +3 -0
- package/components/use-editor-selection.js +16 -0
- package/components/use-voice-playback.d.ts +16 -0
- package/components/use-voice-playback.js +68 -0
- package/hooks/use-described.d.ts +8 -0
- package/hooks/use-described.js +23 -0
- package/hooks/use-theorem-chat-actions.d.ts +75 -0
- package/hooks/use-theorem-chat-actions.js +265 -0
- package/hooks/use-theorem-chat-state.d.ts +66 -0
- package/hooks/use-theorem-chat-state.js +118 -0
- package/hooks/use-theorem-chat.d.ts +63 -0
- package/hooks/use-theorem-chat.js +340 -0
- package/hooks/use-theorem-decision.d.ts +23 -0
- package/hooks/use-theorem-decision.js +75 -0
- package/hooks/use-theorem-host.d.ts +44 -0
- package/hooks/use-theorem-host.js +117 -0
- package/hooks/use-theorem-interface.d.ts +19 -0
- package/hooks/use-theorem-interface.js +25 -0
- package/hooks/use-trace-records.d.ts +4 -0
- package/hooks/use-trace-records.js +7 -0
- package/index.d.ts +14 -0
- package/index.js +14 -0
- package/live.d.ts +8 -0
- package/live.js +10 -0
- package/package.json +73 -4
- package/server/credential-store.d.ts +24 -0
- package/server/credential-store.js +14 -0
- package/server/decision-handler.d.ts +34 -0
- package/server/decision-handler.js +55 -0
- package/server/handler.d.ts +114 -0
- package/server/handler.js +568 -0
- package/server/json-response.d.ts +2 -0
- package/server/json-response.js +7 -0
- package/server/mod.d.ts +16 -0
- package/server/mod.js +15 -0
- package/server/request-check.d.ts +16 -0
- package/server/request-check.js +34 -0
- package/server/session-store.d.ts +74 -0
- package/server/session-store.js +49 -0
- package/server/steer-inbox.d.ts +35 -0
- package/server/steer-inbox.js +52 -0
- package/server/walk-away.d.ts +22 -0
- package/server/walk-away.js +41 -0
- package/styles/ink-controls.css +134 -0
- package/ui/ChatComposerBar.d.ts +39 -0
- package/ui/ChatComposerBar.js +251 -0
- package/ui/ChatTranscript.d.ts +30 -0
- package/ui/ChatTranscript.js +474 -0
- package/ui/ConsoleFrame.d.ts +47 -0
- package/ui/ConsoleFrame.js +46 -0
- package/ui/DecisionAnswers.d.ts +15 -0
- package/ui/DecisionAnswers.js +102 -0
- package/ui/LiveRunner.d.ts +28 -0
- package/ui/LiveRunner.js +193 -0
- package/ui/SchemaFields.d.ts +10 -0
- package/ui/SchemaFields.js +122 -0
- package/ui/ShapedData.d.ts +11 -0
- package/ui/ShapedData.js +151 -0
- package/ui/SidePanel.d.ts +57 -0
- package/ui/SidePanel.js +50 -0
- package/ui/StateFields.d.ts +36 -0
- package/ui/StateFields.js +96 -0
- package/ui/TheoremChat.d.ts +71 -0
- package/ui/TheoremChat.js +146 -0
- package/ui/TheoremDecision.d.ts +38 -0
- package/ui/TheoremDecision.js +87 -0
- package/ui/TheoremHost.d.ts +38 -0
- package/ui/TheoremHost.js +150 -0
- package/ui/ToolGateCard.d.ts +28 -0
- package/ui/ToolGateCard.js +100 -0
- package/ui/ToolResult.d.ts +10 -0
- package/ui/ToolResult.js +60 -0
- package/ui/ToolResultCharts.d.ts +5 -0
- package/ui/ToolResultCharts.js +86 -0
- package/ui/TraceCharts.d.ts +13 -0
- package/ui/TraceCharts.js +235 -0
- package/ui/TraceGuardrails.d.ts +10 -0
- package/ui/TraceGuardrails.js +56 -0
- package/ui/TraceInspector.d.ts +6 -0
- package/ui/TraceInspector.js +189 -0
- package/ui/TraceInspectorPanel.d.ts +24 -0
- package/ui/TraceInspectorPanel.js +57 -0
- package/ui/TraceOverview.d.ts +34 -0
- package/ui/TraceOverview.js +96 -0
- package/ui/TraceSpanDetail.d.ts +10 -0
- package/ui/TraceSpanDetail.js +188 -0
- package/ui/TraceStory.d.ts +20 -0
- package/ui/TraceStory.js +107 -0
- package/ui/TraceValues.d.ts +36 -0
- package/ui/TraceValues.js +113 -0
- package/ui/TraceWaterfall.d.ts +13 -0
- package/ui/TraceWaterfall.js +132 -0
- package/ui/VoiceNote.d.ts +12 -0
- package/ui/VoiceNote.js +28 -0
- package/ui/built/theme.css +774 -0
- package/ui/built/theorem.d.ts +13 -0
- package/ui/built/theorem.js +631 -0
- package/ui/built/theorem.variants.d.ts +16 -0
- package/ui/disclosure-motion.d.ts +6 -0
- package/ui/disclosure-motion.js +163 -0
- package/ui/icons.d.ts +4 -0
- package/ui/icons.js +37 -0
- package/ui/index.d.ts +25 -0
- package/ui/index.js +29 -0
- package/ui/labels-provider.d.ts +16 -0
- package/ui/labels-provider.js +61 -0
- package/ui/labels.d.ts +1153 -0
- package/ui/labels.js +603 -0
- package/ui/theme.d.ts +10 -0
- package/ui/theme.js +13 -0
- package/ui/theorem-theme.d.ts +10 -0
- package/ui/theorem-theme.js +83 -0
- package/ui/trace-palette.d.ts +24 -0
- package/ui/trace-palette.js +24 -0
- package/ui/trace-patterns.d.ts +32 -0
- package/ui/trace-patterns.js +44 -0
- package/ui/transcript-copy-text.d.ts +4 -0
- package/ui/transcript-copy-text.js +39 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The live wire between the browser and a host's relay: the messages the live
|
|
3
|
+
* client sends (a relay reads them with `parseLiveClientMessage`) and the envelopes it reads
|
|
4
|
+
* back, each checked against its schema.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import { type GateDecision, type ToolGate, type TraceRecord, type TurnEvent, z } from '@theoremjs/agents';
|
|
9
|
+
import { type HostErrorBody } from './transport.ts';
|
|
10
|
+
import { type MalformedEvent, type UnsupportedEvent } from './wire-line.ts';
|
|
11
|
+
/** What the live client sends its relay, besides the host's own `openMessage`. */
|
|
12
|
+
export type LiveClientMessage = {
|
|
13
|
+
type: 'audio';
|
|
14
|
+
data: string;
|
|
15
|
+
} | {
|
|
16
|
+
type: 'video';
|
|
17
|
+
data: string;
|
|
18
|
+
mimeType: string;
|
|
19
|
+
} | {
|
|
20
|
+
type: 'text';
|
|
21
|
+
text: string;
|
|
22
|
+
}
|
|
23
|
+
/** Background the model reads without replying: the page the visitor is on, say. */
|
|
24
|
+
| {
|
|
25
|
+
type: 'context';
|
|
26
|
+
text: string;
|
|
27
|
+
}
|
|
28
|
+
/** Run a call the model made, by its id; the live session holds its name, input and gate. */
|
|
29
|
+
| {
|
|
30
|
+
type: 'executeTool';
|
|
31
|
+
callId: string;
|
|
32
|
+
decision?: GateDecision;
|
|
33
|
+
input?: unknown;
|
|
34
|
+
secret?: string;
|
|
35
|
+
/** What the browser did for a tool that runs there; a relay hands it to the tool as `host.clientOutput`. */
|
|
36
|
+
output?: unknown;
|
|
37
|
+
};
|
|
38
|
+
/** The live client's messages, as `parseLiveClientMessage` checks them for a relay. */
|
|
39
|
+
export declare const liveClientMessageSchema: z.ZodType<LiveClientMessage>;
|
|
40
|
+
/** The envelopes a relay sends the live client. */
|
|
41
|
+
export type LiveServerEnvelope = {
|
|
42
|
+
type: 'ready';
|
|
43
|
+
profile?: string;
|
|
44
|
+
sessionId?: string;
|
|
45
|
+
}
|
|
46
|
+
/** The session's events; one of a kind this client does not know arrives as `unsupported`, one that fails its check as `malformed`. */
|
|
47
|
+
| {
|
|
48
|
+
type: 'events';
|
|
49
|
+
events: (TurnEvent | UnsupportedEvent | MalformedEvent)[];
|
|
50
|
+
}
|
|
51
|
+
/** A trace record the session wrote, from a relay that delivers its traces (the playground). */
|
|
52
|
+
| {
|
|
53
|
+
type: 'trace';
|
|
54
|
+
record: TraceRecord;
|
|
55
|
+
}
|
|
56
|
+
/** The relay's error body, read as a host error. */
|
|
57
|
+
| ({
|
|
58
|
+
type: 'error';
|
|
59
|
+
} & HostErrorBody)
|
|
60
|
+
/**
|
|
61
|
+
* The relay's answer to the browser's `executeTool`: the session settled the
|
|
62
|
+
* call (its tool events carry what happened), holds it on a gate, or refused
|
|
63
|
+
* the message (the relay's error body, read as a host error).
|
|
64
|
+
*/
|
|
65
|
+
| {
|
|
66
|
+
type: 'executeToolResult';
|
|
67
|
+
callId: string;
|
|
68
|
+
status: 'settled';
|
|
69
|
+
} | {
|
|
70
|
+
type: 'executeToolResult';
|
|
71
|
+
callId: string;
|
|
72
|
+
status: 'gated';
|
|
73
|
+
gate: ToolGate;
|
|
74
|
+
} | {
|
|
75
|
+
type: 'executeToolResult';
|
|
76
|
+
callId: string;
|
|
77
|
+
status: 'refused';
|
|
78
|
+
body: HostErrorBody;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* One envelope's text from the relay, checked: an envelope or event of a kind
|
|
82
|
+
* this client does not know is `unsupported`, and one that fails its check is
|
|
83
|
+
* `malformed`. A malformed event leaves the envelope's other events standing.
|
|
84
|
+
*/
|
|
85
|
+
export declare function parseLiveServerEnvelope(text: string): LiveServerEnvelope | UnsupportedEvent | MalformedEvent;
|
|
86
|
+
/** What the session did with an `executeTool`: settled the call, or holds it on a gate. */
|
|
87
|
+
export type LiveToolStep = {
|
|
88
|
+
status: 'settled';
|
|
89
|
+
} | {
|
|
90
|
+
status: 'gated';
|
|
91
|
+
gate: ToolGate;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Ask the relay to run `LiveSession.executeTool` for a call the model made.
|
|
95
|
+
* `decision` answers its gate; `input` is the user's edit to an approval;
|
|
96
|
+
* `secret` is the key the user typed at a sign-in gate, sent once: the
|
|
97
|
+
* session makes it the credential for the gate's slot. `output` is the
|
|
98
|
+
* browser's result for a tool that runs in the page (see `browserToolHandler`).
|
|
99
|
+
*/
|
|
100
|
+
export type ExecuteToolOnRelay = (args: Omit<Extract<LiveClientMessage, {
|
|
101
|
+
type: 'executeTool';
|
|
102
|
+
}>, 'type'>) => Promise<LiveToolStep>;
|
|
103
|
+
export type LiveSocket = Pick<WebSocket, 'readyState' | 'binaryType' | 'send' | 'close' | 'onopen' | 'onmessage' | 'onclose' | 'onerror'>;
|
|
104
|
+
export type LiveConnection = ({
|
|
105
|
+
profile: string;
|
|
106
|
+
} | {
|
|
107
|
+
openMessage: Record<string, unknown>;
|
|
108
|
+
}) & {
|
|
109
|
+
createSocket?: () => LiveSocket;
|
|
110
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The live wire between the browser and a host's relay: the messages the live
|
|
3
|
+
* client sends (a relay reads them with `parseLiveClientMessage`) and the envelopes it reads
|
|
4
|
+
* back, each checked against its schema.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import { GATE_DECISIONS, toolGateSchema, traceRecordSchema, TURN_EVENT_SCHEMAS, z, } from '@theoremjs/agents';
|
|
9
|
+
import { hostErrorBodySchema } from "./transport.js";
|
|
10
|
+
import { readWireLine, readWireValue, } from "./wire-line.js";
|
|
11
|
+
/** The longest context message a relay accepts. */
|
|
12
|
+
const LIVE_CONTEXT_MAX_CHARS = 2000;
|
|
13
|
+
const liveClientMessage = z.discriminatedUnion('type', [
|
|
14
|
+
z.object({ type: z.literal('audio'), data: z.string() }),
|
|
15
|
+
z.object({ type: z.literal('video'), data: z.string(), mimeType: z.string(),
|
|
16
|
+
}),
|
|
17
|
+
z.object({ type: z.literal('text'), text: z.string() }),
|
|
18
|
+
z.object({ type: z.literal('context'), text: z.string().max(LIVE_CONTEXT_MAX_CHARS) }),
|
|
19
|
+
z.object({
|
|
20
|
+
type: z.literal('executeTool'),
|
|
21
|
+
callId: z.string().min(1),
|
|
22
|
+
decision: z.enum(GATE_DECISIONS).optional(),
|
|
23
|
+
input: z.unknown().optional(),
|
|
24
|
+
secret: z.string().optional(),
|
|
25
|
+
output: z.unknown().optional(),
|
|
26
|
+
}),
|
|
27
|
+
]);
|
|
28
|
+
true;
|
|
29
|
+
/** The live client's messages, as `parseLiveClientMessage` checks them for a relay. */
|
|
30
|
+
export const liveClientMessageSchema = liveClientMessage;
|
|
31
|
+
const liveServerEnvelopeKinds = {
|
|
32
|
+
ready: z.object({ type: z.literal('ready'), profile: z.string().optional(), sessionId: z.string().optional(),
|
|
33
|
+
}),
|
|
34
|
+
events: z.object({ type: z.literal('events'), events: z.array(z.unknown()) }),
|
|
35
|
+
trace: z.object({ type: z.literal('trace'), record: traceRecordSchema }),
|
|
36
|
+
error: z.object({ type: z.literal('error') }).and(hostErrorBodySchema),
|
|
37
|
+
executeToolResult: z.discriminatedUnion('status', [
|
|
38
|
+
z.object({ type: z.literal('executeToolResult'), callId: z.string(), status: z.literal('settled'),
|
|
39
|
+
}),
|
|
40
|
+
z.object({
|
|
41
|
+
type: z.literal('executeToolResult'),
|
|
42
|
+
callId: z.string(),
|
|
43
|
+
status: z.literal('gated'),
|
|
44
|
+
gate: toolGateSchema,
|
|
45
|
+
}),
|
|
46
|
+
z.object({
|
|
47
|
+
type: z.literal('executeToolResult'),
|
|
48
|
+
callId: z.string(),
|
|
49
|
+
status: z.literal('refused'),
|
|
50
|
+
body: hostErrorBodySchema,
|
|
51
|
+
}),
|
|
52
|
+
]),
|
|
53
|
+
};
|
|
54
|
+
true;
|
|
55
|
+
const liveServerEnvelopeLines = liveServerEnvelopeKinds;
|
|
56
|
+
/**
|
|
57
|
+
* One envelope's text from the relay, checked: an envelope or event of a kind
|
|
58
|
+
* this client does not know is `unsupported`, and one that fails its check is
|
|
59
|
+
* `malformed`. A malformed event leaves the envelope's other events standing.
|
|
60
|
+
*/
|
|
61
|
+
export function parseLiveServerEnvelope(text) {
|
|
62
|
+
const envelope = readWireLine(liveServerEnvelopeLines, text);
|
|
63
|
+
if (envelope.type !== 'events')
|
|
64
|
+
return envelope;
|
|
65
|
+
return { type: 'events', events: envelope.events.map((event) => readWireValue(TURN_EVENT_SCHEMAS, event)),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** The processor name `worklets/mic-capture.js` registers, for `AudioWorkletNode`. */
|
|
2
|
+
export declare const MIC_CAPTURE_PROCESSOR = "mic-capture-processor";
|
|
3
|
+
/** The worklet module for `audioWorklet.addModule`; bundlers ship it as an asset. */
|
|
4
|
+
export declare function micCaptureWorkletUrl(): URL;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** The processor name `worklets/mic-capture.js` registers, for `AudioWorkletNode`. */
|
|
2
|
+
export const MIC_CAPTURE_PROCESSOR = 'mic-capture-processor';
|
|
3
|
+
/** The worklet module for `audioWorklet.addModule`; bundlers ship it as an asset. */
|
|
4
|
+
export function micCaptureWorkletUrl() {
|
|
5
|
+
return new URL('./worklets/mic-capture.js', import.meta.url);
|
|
6
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The OAuth sign-in popup's word back to the chat that opened it.
|
|
3
|
+
*
|
|
4
|
+
* The host's callback route saves the token on the server, then its page calls
|
|
5
|
+
* `notifyOAuthComplete(slot)`. The chat takes the message only from the popup
|
|
6
|
+
* it opened, on its own origin, for the slot its gate waits for, and resumes
|
|
7
|
+
* the gate: no token ever reaches the browser.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Tell the chat that opened this popup that `slot`'s token is saved. Call it on
|
|
13
|
+
* the callback page once the credential is in the host's credential store. On a
|
|
14
|
+
* page no chat opened, it does nothing.
|
|
15
|
+
*/
|
|
16
|
+
export declare function notifyOAuthComplete(slot: string): void;
|
|
17
|
+
/** The message is `popup`'s, from `origin` (the chat page's own), saying `slot` is signed in. */
|
|
18
|
+
export declare function isOAuthComplete(event: {
|
|
19
|
+
data: unknown;
|
|
20
|
+
source: unknown;
|
|
21
|
+
origin: string;
|
|
22
|
+
}, expected: {
|
|
23
|
+
popup: unknown;
|
|
24
|
+
slot: string;
|
|
25
|
+
origin: string;
|
|
26
|
+
}): boolean;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The OAuth sign-in popup's word back to the chat that opened it.
|
|
3
|
+
*
|
|
4
|
+
* The host's callback route saves the token on the server, then its page calls
|
|
5
|
+
* `notifyOAuthComplete(slot)`. The chat takes the message only from the popup
|
|
6
|
+
* it opened, on its own origin, for the slot its gate waits for, and resumes
|
|
7
|
+
* the gate: no token ever reaches the browser.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import { isRecord } from '@theoremjs/agents/kernel';
|
|
12
|
+
const OAUTH_COMPLETE = 'theorem.oauth_complete';
|
|
13
|
+
/**
|
|
14
|
+
* Tell the chat that opened this popup that `slot`'s token is saved. Call it on
|
|
15
|
+
* the callback page once the credential is in the host's credential store. On a
|
|
16
|
+
* page no chat opened, it does nothing.
|
|
17
|
+
*/
|
|
18
|
+
export function notifyOAuthComplete(slot) {
|
|
19
|
+
const opener = Reflect.get(globalThis, 'opener');
|
|
20
|
+
if (!isRecord(opener) || typeof opener.postMessage !== 'function')
|
|
21
|
+
return;
|
|
22
|
+
opener.postMessage({ type: OAUTH_COMPLETE, slot }, globalThis.location.origin);
|
|
23
|
+
}
|
|
24
|
+
/** The message is `popup`'s, from `origin` (the chat page's own), saying `slot` is signed in. */
|
|
25
|
+
export function isOAuthComplete(event, expected) {
|
|
26
|
+
if (event.source !== expected.popup || event.origin !== expected.origin)
|
|
27
|
+
return false;
|
|
28
|
+
const data = event.data;
|
|
29
|
+
return isRecord(data) && data.type === OAUTH_COMPLETE && data.slot === expected.slot;
|
|
30
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Resample Float32 audio buffer from inputRate to outputRate and convert to Int16 PCM. */
|
|
2
|
+
export declare function downsampleAndConvertToInt16(inputData: Float32Array, inputRate: number, outputRate?: number): Int16Array;
|
|
3
|
+
/** Decode little-endian PCM16 bytes to Float32 samples in [-1, 1]. */
|
|
4
|
+
export declare function pcm16BytesToFloat32(bytes: Uint8Array<ArrayBufferLike>): Float32Array;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Resample Float32 audio buffer from inputRate to outputRate and convert to Int16 PCM. */
|
|
2
|
+
export function downsampleAndConvertToInt16(inputData, inputRate, outputRate = 16000) {
|
|
3
|
+
if (inputRate === outputRate) {
|
|
4
|
+
return float32ToInt16(inputData);
|
|
5
|
+
}
|
|
6
|
+
const ratio = inputRate / outputRate;
|
|
7
|
+
const newLength = Math.round(inputData.length / ratio);
|
|
8
|
+
const result = new Int16Array(newLength);
|
|
9
|
+
let offsetResult = 0;
|
|
10
|
+
let offsetInput = 0;
|
|
11
|
+
while (offsetResult < result.length) {
|
|
12
|
+
const nextOffsetInput = Math.round((offsetResult + 1) * ratio);
|
|
13
|
+
let accum = 0;
|
|
14
|
+
let count = 0;
|
|
15
|
+
for (let i = offsetInput; i < nextOffsetInput && i < inputData.length; i++) {
|
|
16
|
+
accum += inputData[i] ?? 0;
|
|
17
|
+
count++;
|
|
18
|
+
}
|
|
19
|
+
result[offsetResult] = floatSampleToInt16(count > 0 ? accum / count : 0);
|
|
20
|
+
offsetResult++;
|
|
21
|
+
offsetInput = nextOffsetInput;
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
function float32ToInt16(inputData) {
|
|
26
|
+
const result = new Int16Array(inputData.length);
|
|
27
|
+
for (let i = 0; i < inputData.length; i++) {
|
|
28
|
+
result[i] = floatSampleToInt16(inputData[i] ?? 0);
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
function floatSampleToInt16(val) {
|
|
33
|
+
const s = Math.max(-1, Math.min(1, val));
|
|
34
|
+
return s < 0 ? s * 0x8000 : s * 0x7fff;
|
|
35
|
+
}
|
|
36
|
+
/** Decode little-endian PCM16 bytes to Float32 samples in [-1, 1]. */
|
|
37
|
+
export function pcm16BytesToFloat32(bytes) {
|
|
38
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
39
|
+
const float32 = new Float32Array(bytes.byteLength / 2);
|
|
40
|
+
for (let i = 0; i < float32.length; i++) {
|
|
41
|
+
const sample = view.getInt16(i * 2, true);
|
|
42
|
+
float32[i] = sample / (sample < 0 ? 0x8000 : 0x7fff);
|
|
43
|
+
}
|
|
44
|
+
return float32;
|
|
45
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { TheoremError, TurnBlob, TurnEvent } from '@theoremjs/agents';
|
|
2
|
+
import { type ComposerProfileInterface, type InterfaceTurnSession, type TranscriptBlock, type UserTurnHistoryMedia } from '@theoremjs/agents/interface';
|
|
3
|
+
import { type TurnFailure } from './failure.ts';
|
|
4
|
+
import type { TheoremTransport, TurnEventSink } from './transport.ts';
|
|
5
|
+
/** The turn is over: every event it streamed, gate resumes included, enters history once. */
|
|
6
|
+
export declare function commitCompletedTurn(session: InterfaceTurnSession, events: TurnEvent[], media?: UserTurnHistoryMedia): InterfaceTurnSession;
|
|
7
|
+
export declare function toTurnMedia(encodedAttachments?: TurnBlob[], encodedVoice?: TurnBlob[]): UserTurnHistoryMedia;
|
|
8
|
+
/** Where a run shows itself as it streams: the reply so far, and each line the stream left out. */
|
|
9
|
+
export type StreamView = {
|
|
10
|
+
blocks(blocks: TranscriptBlock[]): void;
|
|
11
|
+
/** A `malformed` line: left out, and the reply goes on. */
|
|
12
|
+
skipped(error: TheoremError): void;
|
|
13
|
+
};
|
|
14
|
+
export declare function continueAfterTool(args: {
|
|
15
|
+
iface: ComposerProfileInterface;
|
|
16
|
+
transport: TheoremTransport;
|
|
17
|
+
session: InterfaceTurnSession;
|
|
18
|
+
view: StreamView;
|
|
19
|
+
seedEvents: TurnEvent[];
|
|
20
|
+
}): Promise<{
|
|
21
|
+
ok: true;
|
|
22
|
+
session: InterfaceTurnSession;
|
|
23
|
+
assistantBlocks: TranscriptBlock[];
|
|
24
|
+
} | TurnFailure>;
|
|
25
|
+
/** A turn that failed partway, committed as far as it got. */
|
|
26
|
+
export declare function failTurnStream(args: {
|
|
27
|
+
session: InterfaceTurnSession;
|
|
28
|
+
events: TurnEvent[];
|
|
29
|
+
media: UserTurnHistoryMedia;
|
|
30
|
+
failure: TurnFailure;
|
|
31
|
+
}): TurnFailure;
|
|
32
|
+
/**
|
|
33
|
+
* An answer to `callId`'s gate that failed. Once the call settled, the reply
|
|
34
|
+
* commits as far as it got, or waits on its next gate; before, the reply waits
|
|
35
|
+
* on the gate as it did (the host puts the call back), and the answer can go again.
|
|
36
|
+
*/
|
|
37
|
+
export declare function failGateAnswer(args: {
|
|
38
|
+
/** The paused session the answer went out from. */
|
|
39
|
+
session: InterfaceTurnSession;
|
|
40
|
+
callId: string;
|
|
41
|
+
/** The paused reply's events, then the answer's as far as they came. */
|
|
42
|
+
events: TurnEvent[];
|
|
43
|
+
failure: TurnFailure;
|
|
44
|
+
}): TurnFailure;
|
|
45
|
+
export declare function finalizeTurnStream(args: {
|
|
46
|
+
session: InterfaceTurnSession;
|
|
47
|
+
events: TurnEvent[];
|
|
48
|
+
media: UserTurnHistoryMedia;
|
|
49
|
+
}): InterfaceTurnSession;
|
|
50
|
+
export declare function streamFoldedTurn(args: {
|
|
51
|
+
iface: ComposerProfileInterface;
|
|
52
|
+
view: StreamView;
|
|
53
|
+
/** Seeded by the caller, and filled as the stream delivers. */
|
|
54
|
+
events: TurnEvent[];
|
|
55
|
+
stream: (onEvent: TurnEventSink) => Promise<void>;
|
|
56
|
+
}): Promise<TurnEvent[]>;
|
|
57
|
+
/** `blocks` with its last `turn-done` carrying the reply's work. */
|
|
58
|
+
export declare function stampWorked(blocks: TranscriptBlock[], worked: {
|
|
59
|
+
workedMs: number;
|
|
60
|
+
endedAt: number;
|
|
61
|
+
}): TranscriptBlock[];
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { appendAssistantEventsToHistory, appendUserDraftToHistory, applyTurnEventsToSession, gatedToolFromEvents, settlesToolCall, } from '@theoremjs/agents/interface';
|
|
2
|
+
import { turnFailure } from "./failure.js";
|
|
3
|
+
import { buildTurnRequest, foldAssistantTurn, turnInputFromSession } from "./turn-client.js";
|
|
4
|
+
/** The turn's history before its reply: a turn not yet started adds the user's message. */
|
|
5
|
+
function turnBaseHistory(session, media) {
|
|
6
|
+
return session.pendingUserDraft
|
|
7
|
+
? appendUserDraftToHistory(session.history, session.pendingUserDraft, media)
|
|
8
|
+
: session.history;
|
|
9
|
+
}
|
|
10
|
+
/** The turn is over: every event it streamed, gate resumes included, enters history once. */
|
|
11
|
+
export function commitCompletedTurn(session, events, media = {}) {
|
|
12
|
+
return {
|
|
13
|
+
...applyTurnEventsToSession(session, events),
|
|
14
|
+
history: appendAssistantEventsToHistory(turnBaseHistory(session, media), events),
|
|
15
|
+
gatedTool: null,
|
|
16
|
+
assistantEvents: [],
|
|
17
|
+
pendingUserDraft: null,
|
|
18
|
+
toolSnapshot: undefined,
|
|
19
|
+
promotedToolIds: [],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The turn waits at a gate. History stops at the user's message and the events
|
|
24
|
+
* wait in `assistantEvents`, so the gated step enters history whole once it settles.
|
|
25
|
+
*/
|
|
26
|
+
function pauseTurn(session, events, media = {}) {
|
|
27
|
+
return {
|
|
28
|
+
...applyTurnEventsToSession(session, events),
|
|
29
|
+
history: turnBaseHistory(session, media),
|
|
30
|
+
gatedTool: gatedToolFromEvents(events),
|
|
31
|
+
assistantEvents: [...events],
|
|
32
|
+
pendingUserDraft: null,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function toTurnMedia(encodedAttachments, encodedVoice) {
|
|
36
|
+
return {
|
|
37
|
+
...(encodedAttachments?.length ? { attachments: encodedAttachments } : {}),
|
|
38
|
+
...(encodedVoice?.length ? { voice: encodedVoice } : {}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A failed turn with the conversation it leaves: the user's message and what the
|
|
43
|
+
* reply got through (its text and the tool calls that settled), so the next turn
|
|
44
|
+
* picks up from there. A stopped reply leaves the transcript, so only the message stays.
|
|
45
|
+
*/
|
|
46
|
+
function withFailedTurnSession(failure, commit, events, seedEvents = []) {
|
|
47
|
+
return { ...failure, session: commit(failure.aborted ? seedEvents : events) };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Streams into `events`, which the caller owns, so a stream that fails still
|
|
51
|
+
* leaves what it delivered. An `unsupported` line is the host's to read on its
|
|
52
|
+
* own transport, and a `malformed` one goes to `view.skipped`: neither enters
|
|
53
|
+
* the turn, its history or its transcript.
|
|
54
|
+
*/
|
|
55
|
+
async function streamFoldedEvents(stream, view, iface, events) {
|
|
56
|
+
await stream((event) => {
|
|
57
|
+
if (event.type === 'unsupported')
|
|
58
|
+
return;
|
|
59
|
+
if (event.type === 'malformed') {
|
|
60
|
+
view.skipped(event.error);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
events.push(event);
|
|
64
|
+
view.blocks(foldAssistantTurn(iface, events));
|
|
65
|
+
});
|
|
66
|
+
return events;
|
|
67
|
+
}
|
|
68
|
+
export async function continueAfterTool(args) {
|
|
69
|
+
const events = [...args.seedEvents];
|
|
70
|
+
// The model reads the turn so far: the settled gate and everything before it.
|
|
71
|
+
const sent = {
|
|
72
|
+
...args.session,
|
|
73
|
+
history: appendAssistantEventsToHistory(args.session.history, args.seedEvents),
|
|
74
|
+
};
|
|
75
|
+
try {
|
|
76
|
+
await streamFoldedEvents((onEvent) => args.transport.turn(buildTurnRequest(args.iface, sent, turnInputFromSession(sent)), onEvent), args.view, args.iface, events);
|
|
77
|
+
if (gatedToolFromEvents(events)) {
|
|
78
|
+
return {
|
|
79
|
+
ok: true,
|
|
80
|
+
session: pauseTurn(args.session, events),
|
|
81
|
+
assistantBlocks: foldAssistantTurn(args.iface, events),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
ok: true,
|
|
86
|
+
session: commitCompletedTurn(args.session, events),
|
|
87
|
+
assistantBlocks: foldAssistantTurn(args.iface, events),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
return withFailedTurnSession(turnFailure(err, args.iface.lexicon), (kept) => commitCompletedTurn(args.session, kept), events, args.seedEvents);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** A turn that failed partway, committed as far as it got. */
|
|
95
|
+
export function failTurnStream(args) {
|
|
96
|
+
return withFailedTurnSession(args.failure, (kept) => commitCompletedTurn(args.session, kept, args.media), args.events);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* An answer to `callId`'s gate that failed. Once the call settled, the reply
|
|
100
|
+
* commits as far as it got, or waits on its next gate; before, the reply waits
|
|
101
|
+
* on the gate as it did (the host puts the call back), and the answer can go again.
|
|
102
|
+
*/
|
|
103
|
+
export function failGateAnswer(args) {
|
|
104
|
+
const answered = args.events.slice(args.session.assistantEvents.length);
|
|
105
|
+
if (!answered.some((event) => settlesToolCall(event, args.callId)))
|
|
106
|
+
return args.failure;
|
|
107
|
+
return { ...args.failure, session: finalizeTurnStream({ session: args.session, events: args.events, media: {} }) };
|
|
108
|
+
}
|
|
109
|
+
export function finalizeTurnStream(args) {
|
|
110
|
+
if (gatedToolFromEvents(args.events)) {
|
|
111
|
+
return pauseTurn(args.session, args.events, args.media);
|
|
112
|
+
}
|
|
113
|
+
return commitCompletedTurn(args.session, args.events, args.media);
|
|
114
|
+
}
|
|
115
|
+
export function streamFoldedTurn(args) {
|
|
116
|
+
return streamFoldedEvents(args.stream, args.view, args.iface, args.events);
|
|
117
|
+
}
|
|
118
|
+
/** `blocks` with its last `turn-done` carrying the reply's work. */
|
|
119
|
+
export function stampWorked(blocks, worked) {
|
|
120
|
+
const last = blocks.findLastIndex((block) => block.kind === 'turn-done');
|
|
121
|
+
if (last < 0)
|
|
122
|
+
return blocks;
|
|
123
|
+
return blocks.map((block, index) => (index === last ? { ...block, ...worked } : block));
|
|
124
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type ComposerProfileInterface, type InterfaceTurnSession, type TranscriptBlock, type UserTurnDraft } from '@theoremjs/agents/interface';
|
|
2
|
+
import { type TurnFailure } from './failure.ts';
|
|
3
|
+
import { type StreamView } from './run-commit.ts';
|
|
4
|
+
import type { TheoremTransport } from './transport.ts';
|
|
5
|
+
import { type ToolGateResolution } from './tool-resume.ts';
|
|
6
|
+
export type { TurnFailure } from './failure.ts';
|
|
7
|
+
export type { StreamView } from './run-commit.ts';
|
|
8
|
+
export type StreamTurnSuccess = {
|
|
9
|
+
ok: true;
|
|
10
|
+
session: InterfaceTurnSession;
|
|
11
|
+
userBlocks: TranscriptBlock[];
|
|
12
|
+
assistantBlocks: TranscriptBlock[];
|
|
13
|
+
};
|
|
14
|
+
export type StreamInterfaceTurnBaseArgs = {
|
|
15
|
+
iface: ComposerProfileInterface;
|
|
16
|
+
transport: TheoremTransport;
|
|
17
|
+
session: InterfaceTurnSession;
|
|
18
|
+
view: StreamView;
|
|
19
|
+
/**
|
|
20
|
+
* Fires once the message posts, before its reply streams, with the blocks
|
|
21
|
+
* the transcript gains: a walked-away reply's settled blocks, then the
|
|
22
|
+
* message's own (with media preview data).
|
|
23
|
+
*/
|
|
24
|
+
onUserBlocks?: (blocks: TranscriptBlock[]) => void;
|
|
25
|
+
signal?: AbortSignal;
|
|
26
|
+
turnId?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Send while the reply waits on gates: the message walks away from every
|
|
29
|
+
* waiting call in its own request, and the host settles each one cancelled
|
|
30
|
+
* ahead of the reply. `workedMs` is the paused reply's work so far, which
|
|
31
|
+
* its settled blocks keep. Without it, a waiting session refuses the send
|
|
32
|
+
* (`session.gate_pending`).
|
|
33
|
+
*/
|
|
34
|
+
walkAway?: {
|
|
35
|
+
workedMs: number;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
export type StreamInterfaceTurnArgs = StreamInterfaceTurnBaseArgs & {
|
|
39
|
+
text: string;
|
|
40
|
+
pendingFiles: readonly File[];
|
|
41
|
+
pendingVoice: readonly File[];
|
|
42
|
+
};
|
|
43
|
+
export type StreamInterfaceDraftTurnArgs = StreamInterfaceTurnBaseArgs & {
|
|
44
|
+
draft: UserTurnDraft;
|
|
45
|
+
};
|
|
46
|
+
export declare function streamInterfaceTurn(args: StreamInterfaceTurnArgs): Promise<StreamTurnSuccess | TurnFailure>;
|
|
47
|
+
/** Send a turn from an already-encoded pending draft (queue drain / send now). */
|
|
48
|
+
export declare function streamInterfaceDraftTurn(args: StreamInterfaceDraftTurnArgs): Promise<StreamTurnSuccess | TurnFailure>;
|
|
49
|
+
/**
|
|
50
|
+
* Send the user's answer to the gate the session waits on. The host settles
|
|
51
|
+
* the call either way (an approval runs it; a refusal records it declined),
|
|
52
|
+
* so the model and the trace read what the user chose.
|
|
53
|
+
*/
|
|
54
|
+
export declare function resumeInterfaceTool(args: {
|
|
55
|
+
iface: ComposerProfileInterface;
|
|
56
|
+
transport: TheoremTransport;
|
|
57
|
+
session: InterfaceTurnSession;
|
|
58
|
+
resolution: ToolGateResolution;
|
|
59
|
+
view: StreamView;
|
|
60
|
+
}): Promise<{
|
|
61
|
+
ok: true;
|
|
62
|
+
session: InterfaceTurnSession;
|
|
63
|
+
assistantBlocks: TranscriptBlock[];
|
|
64
|
+
} | TurnFailure>;
|
|
65
|
+
export declare function applyTurnResultToTranscript(args: {
|
|
66
|
+
blocks: TranscriptBlock[];
|
|
67
|
+
streamBlocks: TranscriptBlock[];
|
|
68
|
+
session: InterfaceTurnSession;
|
|
69
|
+
userBlocks?: TranscriptBlock[];
|
|
70
|
+
assistantBlocks: TranscriptBlock[];
|
|
71
|
+
/** The reply's work so far, stamped on its latest `turn-done` so the time stays with the blocks. */
|
|
72
|
+
worked?: {
|
|
73
|
+
workedMs: number;
|
|
74
|
+
endedAt: number;
|
|
75
|
+
};
|
|
76
|
+
}): {
|
|
77
|
+
blocks: TranscriptBlock[];
|
|
78
|
+
streamBlocks: TranscriptBlock[];
|
|
79
|
+
session: InterfaceTurnSession;
|
|
80
|
+
};
|