@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,117 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { toolCallsOf, toolSnapshotFromEvents } from '@theoremjs/agents/interface';
|
|
3
|
+
import { clientFailure } from "../client/failure.js";
|
|
4
|
+
import { continueGatedToolInvocation } from "../client/tool-resume.js";
|
|
5
|
+
import { useDescribed } from "./use-described.js";
|
|
6
|
+
function callId() {
|
|
7
|
+
return typeof crypto !== 'undefined' && 'randomUUID' in crypto
|
|
8
|
+
? crypto.randomUUID()
|
|
9
|
+
: `call-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
10
|
+
}
|
|
11
|
+
function withEvent(call, event) {
|
|
12
|
+
const events = [...call.events, event];
|
|
13
|
+
const progress = event.type === 'tool' && event.tool.phase === 'progress' ? event.tool.data : call.progress;
|
|
14
|
+
return { ...call, events, call: toolCallsOf(events)[0] ?? null, progress };
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* One host profile: describe it once, then `run(name, input)` any of its tools
|
|
18
|
+
* as often as needed, and answer the gates a call pauses on. `run` returns the
|
|
19
|
+
* call's id at once; calls run side by side, and `cancel` stops one.
|
|
20
|
+
*/
|
|
21
|
+
export function useTheoremHost(transport) {
|
|
22
|
+
const { iface, describeFailure } = useDescribed(transport);
|
|
23
|
+
const [calls, setCalls] = useState([]);
|
|
24
|
+
const running = useRef(new Map());
|
|
25
|
+
// Tools the user allowed for the rest of the page, for a host that replays paused calls.
|
|
26
|
+
const permissions = useRef([]);
|
|
27
|
+
const latest = useRef(calls);
|
|
28
|
+
latest.current = calls;
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const open = running.current;
|
|
31
|
+
return () => {
|
|
32
|
+
for (const controller of open.values())
|
|
33
|
+
controller.abort();
|
|
34
|
+
};
|
|
35
|
+
}, []);
|
|
36
|
+
const update = useCallback((id, change) => {
|
|
37
|
+
setCalls((previous) => previous.map((call) => (call.id === id ? change(call) : call)));
|
|
38
|
+
}, []);
|
|
39
|
+
/** Streams one request's events into the call, then marks it settled or failed. */
|
|
40
|
+
const stream = useCallback(async (id, send) => {
|
|
41
|
+
const controller = new AbortController();
|
|
42
|
+
running.current.set(id, controller);
|
|
43
|
+
const started = performance.now();
|
|
44
|
+
update(id, (call) => ({ ...call, isRunning: true, failure: null }));
|
|
45
|
+
try {
|
|
46
|
+
await send((event) => {
|
|
47
|
+
if (event.type === 'unsupported' || event.type === 'malformed')
|
|
48
|
+
return;
|
|
49
|
+
update(id, (call) => withEvent(call, event));
|
|
50
|
+
}, controller.signal);
|
|
51
|
+
update(id, (call) => ({ ...call, isRunning: false, answering: null, elapsedMs: performance.now() - started }));
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
const failure = controller.signal.aborted ? null : clientFailure(err);
|
|
55
|
+
update(id, (call) => ({ ...call, isRunning: false, answering: null, failure, elapsedMs: performance.now() - started }));
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
if (running.current.get(id) === controller)
|
|
59
|
+
running.current.delete(id);
|
|
60
|
+
}
|
|
61
|
+
}, [update]);
|
|
62
|
+
const run = useCallback((name, input) => {
|
|
63
|
+
const id = callId();
|
|
64
|
+
setCalls((previous) => [
|
|
65
|
+
{
|
|
66
|
+
id,
|
|
67
|
+
name,
|
|
68
|
+
input,
|
|
69
|
+
events: [],
|
|
70
|
+
call: null,
|
|
71
|
+
progress: undefined,
|
|
72
|
+
isRunning: true,
|
|
73
|
+
failure: null,
|
|
74
|
+
answering: null,
|
|
75
|
+
startedAt: Date.now(),
|
|
76
|
+
elapsedMs: null,
|
|
77
|
+
},
|
|
78
|
+
...previous,
|
|
79
|
+
]);
|
|
80
|
+
void stream(id, (onEvent, signal) => transport.call({ name, input }, onEvent, signal));
|
|
81
|
+
return id;
|
|
82
|
+
}, [stream, transport]);
|
|
83
|
+
const answer = useCallback(async (id, resolution) => {
|
|
84
|
+
const held = latest.current.find((call) => call.id === id);
|
|
85
|
+
const state = held?.call?.state;
|
|
86
|
+
if (!held?.call || state?.phase !== 'gate')
|
|
87
|
+
return;
|
|
88
|
+
const gateId = held.call.callId;
|
|
89
|
+
const reply = continueGatedToolInvocation({
|
|
90
|
+
toolName: held.name,
|
|
91
|
+
gate: state.gate,
|
|
92
|
+
sessionPermissions: permissions.current,
|
|
93
|
+
resolution,
|
|
94
|
+
});
|
|
95
|
+
if (reply.decision === 'approve')
|
|
96
|
+
permissions.current = reply.sessionPermissions;
|
|
97
|
+
const snapshot = toolSnapshotFromEvents(held.events);
|
|
98
|
+
update(id, (call) => ({ ...call, answering: resolution.action }));
|
|
99
|
+
await stream(id, (onEvent, signal) => transport.invoke({
|
|
100
|
+
gateId,
|
|
101
|
+
decision: reply.decision,
|
|
102
|
+
...(reply.decision === 'approve' && reply.secret !== undefined ? { secret: reply.secret } : {}),
|
|
103
|
+
// A host without sessions (the playground) replays the paused call; others ignore it.
|
|
104
|
+
replay: {
|
|
105
|
+
name: held.name,
|
|
106
|
+
input: held.input,
|
|
107
|
+
sessionPermissions: permissions.current,
|
|
108
|
+
...(snapshot ? { snapshot } : {}),
|
|
109
|
+
},
|
|
110
|
+
}, onEvent, signal));
|
|
111
|
+
}, [stream, transport, update]);
|
|
112
|
+
const cancel = useCallback((id) => {
|
|
113
|
+
running.current.get(id)?.abort();
|
|
114
|
+
running.current.delete(id);
|
|
115
|
+
}, []);
|
|
116
|
+
return { iface, describeFailure, calls, run, answer, cancel };
|
|
117
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ProfileInterface } from '@theoremjs/agents/interface';
|
|
2
|
+
import { type ClientFailure } from '../client/failure.ts';
|
|
3
|
+
import type { TheoremTransport } from '../client/transport.ts';
|
|
4
|
+
/** `failure` is worded with the default lexicon: the profile's is not known until it loads. */
|
|
5
|
+
export type TheoremInterfaceState = {
|
|
6
|
+
status: 'loading';
|
|
7
|
+
iface: null;
|
|
8
|
+
failure: null;
|
|
9
|
+
} | {
|
|
10
|
+
status: 'ready';
|
|
11
|
+
iface: ProfileInterface;
|
|
12
|
+
failure: null;
|
|
13
|
+
} | {
|
|
14
|
+
status: 'error';
|
|
15
|
+
iface: null;
|
|
16
|
+
failure: ClientFailure;
|
|
17
|
+
};
|
|
18
|
+
/** Ask the transport for the host profile's client-safe interface. */
|
|
19
|
+
export declare function useTheoremInterface(transport: TheoremTransport): TheoremInterfaceState;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { clientFailure } from "../client/failure.js";
|
|
3
|
+
const LOADING = { status: 'loading', iface: null, failure: null };
|
|
4
|
+
/** Ask the transport for the host profile's client-safe interface. */
|
|
5
|
+
export function useTheoremInterface(transport) {
|
|
6
|
+
const [state, setState] = useState(LOADING);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const controller = new AbortController();
|
|
9
|
+
// A new transport keeps the last interface up until it describes itself, so swapping one
|
|
10
|
+
// in (a recompiled playground profile) doesn't blank the chat to a spinner.
|
|
11
|
+
setState((previous) => (previous.status === 'ready' ? previous : LOADING));
|
|
12
|
+
transport.describe(controller.signal).then((iface) => {
|
|
13
|
+
if (!controller.signal.aborted)
|
|
14
|
+
setState({ status: 'ready', iface, failure: null });
|
|
15
|
+
}, (err) => {
|
|
16
|
+
if (controller.signal.aborted)
|
|
17
|
+
return;
|
|
18
|
+
setState({ status: 'error', iface: null, failure: clientFailure(err) });
|
|
19
|
+
});
|
|
20
|
+
return () => {
|
|
21
|
+
controller.abort();
|
|
22
|
+
};
|
|
23
|
+
}, [transport]);
|
|
24
|
+
return state;
|
|
25
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { TraceRecord } from '@theoremjs/agents';
|
|
2
|
+
import type { TraceFeed } from '../client/trace-feed.ts';
|
|
3
|
+
/** The records a feed has delivered so far; none without a feed. */
|
|
4
|
+
export declare function useTraceRecords(feed: TraceFeed | undefined): readonly TraceRecord[];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { useSyncExternalStore } from 'react';
|
|
2
|
+
const NONE = [];
|
|
3
|
+
const noSubscription = () => () => { };
|
|
4
|
+
/** The records a feed has delivered so far; none without a feed. */
|
|
5
|
+
export function useTraceRecords(feed) {
|
|
6
|
+
return useSyncExternalStore(feed?.subscribe ?? noSubscription, feed?.records ?? (() => NONE));
|
|
7
|
+
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless React bindings for Theorem — hooks and a transport, no styling.
|
|
3
|
+
*
|
|
4
|
+
* - `@theoremjs/react` hooks + transport (this entry)
|
|
5
|
+
* - `@theoremjs/react/ui` Astryx chat UI built on these hooks
|
|
6
|
+
* - `@theoremjs/react/server` `createTheoremHandler` (and `createTheoremDecisionHandler`, `createTheoremHostHandler`) for the host side
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
export * from './client/index.ts';
|
|
11
|
+
export { type ChatSnapshot, type SentTurn, type UseTheoremChatOptions, useTheoremChat, } from './hooks/use-theorem-chat.ts';
|
|
12
|
+
export { type TheoremDecisionState, useTheoremDecision } from './hooks/use-theorem-decision.ts';
|
|
13
|
+
export { type TheoremHostCall, type TheoremHostState, useTheoremHost } from './hooks/use-theorem-host.ts';
|
|
14
|
+
export { type TheoremInterfaceState, useTheoremInterface } from './hooks/use-theorem-interface.ts';
|
package/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless React bindings for Theorem — hooks and a transport, no styling.
|
|
3
|
+
*
|
|
4
|
+
* - `@theoremjs/react` hooks + transport (this entry)
|
|
5
|
+
* - `@theoremjs/react/ui` Astryx chat UI built on these hooks
|
|
6
|
+
* - `@theoremjs/react/server` `createTheoremHandler` (and `createTheoremDecisionHandler`, `createTheoremHostHandler`) for the host side
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
export * from "./client/index.js";
|
|
11
|
+
export { useTheoremChat, } from "./hooks/use-theorem-chat.js";
|
|
12
|
+
export { useTheoremDecision } from "./hooks/use-theorem-decision.js";
|
|
13
|
+
export { useTheoremHost } from "./hooks/use-theorem-host.js";
|
|
14
|
+
export { useTheoremInterface } from "./hooks/use-theorem-interface.js";
|
package/live.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live (voice / video) runner, on the Astryx UI in the Theorem theme.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
export type { LiveConnection } from './client/live-client.ts';
|
|
7
|
+
export type { LiveRunnerProps } from './ui/LiveRunner.tsx';
|
|
8
|
+
export { LiveRunner } from './ui/LiveRunner.tsx';
|
package/live.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live (voice / video) runner, on the Astryx UI in the Theorem theme.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
// Astryx's documented order: reset → components → theme.
|
|
7
|
+
import '@astryxdesign/core/reset.css';
|
|
8
|
+
import '@astryxdesign/core/astryx.css';
|
|
9
|
+
import './ui/built/theme.css';
|
|
10
|
+
export { LiveRunner } from "./ui/LiveRunner.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,75 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theoremjs/react",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "React bindings for Theorem: headless hooks + transport, an Astryx chat UI, and a Web-standard server handler.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=20"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/masudl-hub/theoremai",
|
|
13
|
+
"directory": "react"
|
|
14
|
+
},
|
|
15
|
+
"sideEffects": [
|
|
16
|
+
"**/*.css",
|
|
17
|
+
"./live.js",
|
|
18
|
+
"./ui/index.js"
|
|
19
|
+
],
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./index.d.ts",
|
|
23
|
+
"default": "./index.js"
|
|
24
|
+
},
|
|
25
|
+
"./ui": {
|
|
26
|
+
"types": "./ui/index.d.ts",
|
|
27
|
+
"default": "./ui/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./ui/theme": {
|
|
30
|
+
"types": "./ui/theorem-theme.d.ts",
|
|
31
|
+
"default": "./ui/theorem-theme.js"
|
|
32
|
+
},
|
|
33
|
+
"./ui/icons": {
|
|
34
|
+
"types": "./ui/icons.d.ts",
|
|
35
|
+
"default": "./ui/icons.js"
|
|
36
|
+
},
|
|
37
|
+
"./server": {
|
|
38
|
+
"types": "./server/mod.d.ts",
|
|
39
|
+
"default": "./server/mod.js"
|
|
40
|
+
},
|
|
41
|
+
"./live": {
|
|
42
|
+
"types": "./live.d.ts",
|
|
43
|
+
"default": "./live.js"
|
|
44
|
+
},
|
|
45
|
+
"./client": {
|
|
46
|
+
"types": "./client/index.d.ts",
|
|
47
|
+
"default": "./client/index.js"
|
|
48
|
+
},
|
|
49
|
+
"./styles/*": "./styles/*"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@tabler/icons-react": "^3.46.0",
|
|
53
|
+
"intl-messageformat": "^11.2.9",
|
|
54
|
+
"recharts": "^3.10.1"
|
|
55
|
+
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"@astryxdesign/core": "0.6.3",
|
|
58
|
+
"@astryxdesign/theme-neutral": "0.6.3",
|
|
59
|
+
"@stylexjs/stylex": "0.19.0",
|
|
60
|
+
"react": "^19.0.0",
|
|
61
|
+
"react-dom": "^19.0.0",
|
|
62
|
+
"@theoremjs/agents": "^0.3.0"
|
|
63
|
+
},
|
|
64
|
+
"peerDependenciesMeta": {
|
|
65
|
+
"@astryxdesign/core": {
|
|
66
|
+
"optional": true
|
|
67
|
+
},
|
|
68
|
+
"@astryxdesign/theme-neutral": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"@stylexjs/stylex": {
|
|
72
|
+
"optional": true
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool credentials for `createTheoremHandler`, held on the server by session.
|
|
3
|
+
*
|
|
4
|
+
* The browser never holds a credential: the host's OAuth callback route saves
|
|
5
|
+
* a token here, a key the user types at a gate is saved here as it arrives,
|
|
6
|
+
* and every turn reads a slot from here only when a tool that signs in runs. A
|
|
7
|
+
* refreshed OAuth token is saved here before the call goes on.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import type { ToolCredential } from '@theoremjs/agents/kernel';
|
|
12
|
+
import { type MemorySessionStoreOptions } from './session-store.ts';
|
|
13
|
+
/** A session's credentials, keyed by the tools' auth slot. */
|
|
14
|
+
export type TheoremCredentials = Record<string, ToolCredential>;
|
|
15
|
+
/**
|
|
16
|
+
* Where tool credentials live. The default is process memory: use a vault or
|
|
17
|
+
* an encrypted row when requests can reach different instances, and so the
|
|
18
|
+
* credentials outlive the process.
|
|
19
|
+
*/
|
|
20
|
+
export interface TheoremCredentialStore {
|
|
21
|
+
load(sessionId: string): TheoremCredentials | undefined | Promise<TheoremCredentials | undefined>;
|
|
22
|
+
save(sessionId: string, credentials: TheoremCredentials): void | Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export declare function createMemoryCredentialStore(options?: MemorySessionStoreOptions): TheoremCredentialStore;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool credentials for `createTheoremHandler`, held on the server by session.
|
|
3
|
+
*
|
|
4
|
+
* The browser never holds a credential: the host's OAuth callback route saves
|
|
5
|
+
* a token here, a key the user types at a gate is saved here as it arrives,
|
|
6
|
+
* and every turn reads a slot from here only when a tool that signs in runs. A
|
|
7
|
+
* refreshed OAuth token is saved here before the call goes on.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import { createMemorySessionMap } from "./session-store.js";
|
|
12
|
+
export function createMemoryCredentialStore(options = {}) {
|
|
13
|
+
return createMemorySessionMap(options);
|
|
14
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One Web-standard handler that serves a decision profile to `<TheoremDecision />`.
|
|
3
|
+
*
|
|
4
|
+
* - `GET <base>` → `{ interface }` the profile's handle, contract, and questions
|
|
5
|
+
* - `POST <base>/decide` → `{ result }` the answers for the posted `{ state }`
|
|
6
|
+
*
|
|
7
|
+
* The host owns the questions: the browser sends only the state, so a visitor
|
|
8
|
+
* can't spend the host's key asking questions of their own.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import { type DecisionProfile, type DecisionQuestion, type KeyVault, type TraceSink } from '@theoremjs/agents';
|
|
13
|
+
import type { DecisionProfileDefinition } from '@theoremjs/agents/kernel';
|
|
14
|
+
export type TheoremDecisionHandlerOptions = {
|
|
15
|
+
/** Decision profile to serve. Registered with the kernel when the handler is created. */
|
|
16
|
+
profile: DecisionProfile | DecisionProfileDefinition;
|
|
17
|
+
/** The questions every decision asks, by id. */
|
|
18
|
+
questions: Record<string, DecisionQuestion>;
|
|
19
|
+
/** The host's keys by slot; the profile's key slot reads from it. */
|
|
20
|
+
vault?: KeyVault;
|
|
21
|
+
fetch?: typeof globalThis.fetch;
|
|
22
|
+
/** Replaces the profile's `observability.writeTo` for every decision. */
|
|
23
|
+
sink?: TraceSink;
|
|
24
|
+
/** Host metadata kept on each decision's trace record, e.g. the signed-in user. */
|
|
25
|
+
metadata?: (request: Request) => Record<string, unknown> | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Called with any error the handler catches, for reporting. Users read the
|
|
28
|
+
* profile's lexicon wording for the error's kind, never the error itself.
|
|
29
|
+
*/
|
|
30
|
+
onError?: (err: unknown, ctx: {
|
|
31
|
+
request: Request;
|
|
32
|
+
}) => void;
|
|
33
|
+
};
|
|
34
|
+
export declare function createTheoremDecisionHandler(options: TheoremDecisionHandlerOptions): (request: Request) => Promise<Response>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One Web-standard handler that serves a decision profile to `<TheoremDecision />`.
|
|
3
|
+
*
|
|
4
|
+
* - `GET <base>` → `{ interface }` the profile's handle, contract, and questions
|
|
5
|
+
* - `POST <base>/decide` → `{ result }` the answers for the posted `{ state }`
|
|
6
|
+
*
|
|
7
|
+
* The host owns the questions: the browser sends only the state, so a visitor
|
|
8
|
+
* can't spend the host's key asking questions of their own.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import { defineProfile, errorKind, lexiconText, publicError, registerProfile, runDecision, z, } from '@theoremjs/agents';
|
|
13
|
+
import { caughtStatus, HTTP_METHOD } from '@theoremjs/agents/host';
|
|
14
|
+
import { decisionInterface } from "../client/decision-transport.js";
|
|
15
|
+
import { readBody } from "./handler.js";
|
|
16
|
+
import { jsonResponse } from "./json-response.js";
|
|
17
|
+
const decideBodySchema = z.object({ state: z.unknown() });
|
|
18
|
+
function isDecide(request) {
|
|
19
|
+
return new URL(request.url).pathname.split('/').filter(Boolean).at(-1) === 'decide';
|
|
20
|
+
}
|
|
21
|
+
export function createTheoremDecisionHandler(options) {
|
|
22
|
+
const profile = defineProfile(options.profile);
|
|
23
|
+
if (profile.type !== 'decision') {
|
|
24
|
+
// lexicon-exempt: builder config error at setup; no user sees it
|
|
25
|
+
throw new Error(`createTheoremDecisionHandler serves type 'decision'; got type '${profile.type}'.`);
|
|
26
|
+
}
|
|
27
|
+
registerProfile(profile);
|
|
28
|
+
const iface = decisionInterface(profile, options.questions);
|
|
29
|
+
return async (request) => {
|
|
30
|
+
try {
|
|
31
|
+
if (request.method === 'GET' && !isDecide(request))
|
|
32
|
+
return jsonResponse(200, { interface: iface });
|
|
33
|
+
if (request.method !== 'POST' || !isDecide(request)) {
|
|
34
|
+
return jsonResponse(HTTP_METHOD, {
|
|
35
|
+
error: lexiconText('error.request', {}, profile.lexicon),
|
|
36
|
+
errorKind: 'request',
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const body = await readBody(request, decideBodySchema);
|
|
40
|
+
const result = await runDecision({
|
|
41
|
+
profile: profile.id,
|
|
42
|
+
// The kernel checks the state is non-null JSON within the profile's size.
|
|
43
|
+
state: body.state,
|
|
44
|
+
questions: options.questions,
|
|
45
|
+
signal: request.signal,
|
|
46
|
+
metadata: options.metadata?.(request),
|
|
47
|
+
}, { vault: options.vault, fetch: options.fetch, sink: options.sink });
|
|
48
|
+
return jsonResponse(200, { result });
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
options.onError?.(err, { request });
|
|
52
|
+
return jsonResponse(caughtStatus(err), { error: publicError(err, profile.lexicon), errorKind: errorKind(err) });
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One Web-standard handler that serves a Theorem chat UI for a single profile.
|
|
3
|
+
*
|
|
4
|
+
* Mount it on any framework that speaks `Request` → `Response` (Deno.serve,
|
|
5
|
+
* Hono, Next route handlers, SvelteKit, Workers) under a catch-all route:
|
|
6
|
+
*
|
|
7
|
+
* - `GET <base>` → `{ interface }` client-safe profile interface
|
|
8
|
+
* - `POST <base>/turn` → NDJSON turn events; a message sent while the reply
|
|
9
|
+
* waits on gates walks away from them first (`abandon`)
|
|
10
|
+
* - `POST <base>/invoke` → NDJSON events for the user's answer to a paused tool call
|
|
11
|
+
* - `POST <base>/steer` → inject messages into a running turn
|
|
12
|
+
*
|
|
13
|
+
* `createTheoremHostHandler` serves a host profile the same way, with no model:
|
|
14
|
+
*
|
|
15
|
+
* - `GET <base>` → `{ interface }` the tools the page may call, with their schemas
|
|
16
|
+
* - `POST <base>/call` → NDJSON events for one call of one of those tools
|
|
17
|
+
* - `POST <base>/invoke` → NDJSON events for the user's answer to a paused call
|
|
18
|
+
*
|
|
19
|
+
* Trust boundary: the browser owns the conversation text; the server owns every
|
|
20
|
+
* decision that grants authority. Tool permissions, which paused calls may run
|
|
21
|
+
* (and with what input), and which provider interactions may be continued are
|
|
22
|
+
* kept in a server-side session — request bodies can't grant any of them.
|
|
23
|
+
* Tool credentials are held on the server too: a key the user types at a gate
|
|
24
|
+
* is sent once and saved, and no credential is ever sent back to the browser.
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import { type CreateProviderOptions, type ModelProvider, type Profile, type ProfileDefinition, type ToolGate } from '@theoremjs/agents';
|
|
29
|
+
import { type ClientTurnOptions } from '@theoremjs/agents/host';
|
|
30
|
+
import type { z } from '@theoremjs/agents';
|
|
31
|
+
import { type TheoremCredentialStore } from './credential-store.ts';
|
|
32
|
+
import { type TheoremSessionStore } from './session-store.ts';
|
|
33
|
+
import { type SteerInbox } from './steer-inbox.ts';
|
|
34
|
+
export type TheoremRequestContext = {
|
|
35
|
+
request: Request;
|
|
36
|
+
/** Selected model id, when the client picked one. */
|
|
37
|
+
model?: string;
|
|
38
|
+
};
|
|
39
|
+
export type TheoremHandlerOptions = {
|
|
40
|
+
/** Profile to serve. Registered with the kernel when the handler is created. */
|
|
41
|
+
profile: Profile | ProfileDefinition;
|
|
42
|
+
/**
|
|
43
|
+
* Provider credentials passed to `createProvider`, or a factory for hosts that
|
|
44
|
+
* pick keys per request (BYOK, tenant vaults). Tools are registered by the host.
|
|
45
|
+
*/
|
|
46
|
+
provider: CreateProviderOptions | ((ctx: TheoremRequestContext) => ModelProvider | Promise<ModelProvider>);
|
|
47
|
+
/** Opaque app context for tool handlers (`ctx.host`) — e.g. the signed-in user. */
|
|
48
|
+
host?: (request: Request) => unknown;
|
|
49
|
+
/**
|
|
50
|
+
* Resolve the caller's session id — e.g. `${userId}:${conversationId}` from your
|
|
51
|
+
* auth. Return `undefined` to refuse the request (401). Default: an opaque id
|
|
52
|
+
* in an HttpOnly, SameSite=Lax cookie the handler issues on first contact.
|
|
53
|
+
*/
|
|
54
|
+
session?: (request: Request) => string | undefined | Promise<string | undefined>;
|
|
55
|
+
/** Default: in-process memory. Use a shared store when requests can reach different instances. */
|
|
56
|
+
sessionStore?: TheoremSessionStore;
|
|
57
|
+
/**
|
|
58
|
+
* How long a gate (permission, confirmation, sign-in) waits for its answer, in
|
|
59
|
+
* milliseconds. An answer after that is refused with `session.gate_expired`.
|
|
60
|
+
* Default: 30 minutes.
|
|
61
|
+
*/
|
|
62
|
+
gateTtlMs?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Tool credentials by session. Your OAuth callback route saves the token here
|
|
65
|
+
* under the same session id; every turn reads from here. Default: in-process
|
|
66
|
+
* memory — pass the same instance to your callback route.
|
|
67
|
+
*/
|
|
68
|
+
credentialStore?: TheoremCredentialStore;
|
|
69
|
+
/**
|
|
70
|
+
* The sign-in URL for an OAuth gate: begin the flow with `createOAuthPkceFlow`,
|
|
71
|
+
* binding it to `sessionId`, and return its `authorizationUrl`. Without it an
|
|
72
|
+
* OAuth gate carries no sign-in URL.
|
|
73
|
+
*/
|
|
74
|
+
authorizationUrl?: (challenge: Extract<ToolGate, {
|
|
75
|
+
kind: 'auth';
|
|
76
|
+
}>['authChallenge'], ctx: {
|
|
77
|
+
request: Request;
|
|
78
|
+
sessionId: string;
|
|
79
|
+
}) => string | Promise<string>;
|
|
80
|
+
/** Default: in-process memory. Use a shared store when turns and steers can land on different instances. */
|
|
81
|
+
steerInbox?: SteerInbox;
|
|
82
|
+
/** Forwarded to `forClient` when projecting events for the browser. */
|
|
83
|
+
clientEvents?: ClientTurnOptions;
|
|
84
|
+
/**
|
|
85
|
+
* Called with any error the handler catches, for reporting. Users read the
|
|
86
|
+
* profile's lexicon wording for the error's kind, never the error itself.
|
|
87
|
+
*/
|
|
88
|
+
onError?: (err: unknown, ctx: {
|
|
89
|
+
request: Request;
|
|
90
|
+
}) => void;
|
|
91
|
+
};
|
|
92
|
+
/** A host profile runs no model, and takes no turns to steer. */
|
|
93
|
+
export type TheoremHostHandlerOptions = Omit<TheoremHandlerOptions, 'profile' | 'provider' | 'steerInbox'> & {
|
|
94
|
+
/** Host profile to serve. Registered with the kernel when the handler is created. */
|
|
95
|
+
profile: Extract<Profile, {
|
|
96
|
+
type: 'host';
|
|
97
|
+
}> | Extract<ProfileDefinition, {
|
|
98
|
+
type: 'host';
|
|
99
|
+
}>;
|
|
100
|
+
};
|
|
101
|
+
/** A JSON body checked against `schema`; a missing or malformed field is a `request` error (400). */
|
|
102
|
+
export declare function readBody<T>(request: Request, schema: z.ZodType<T>): Promise<T>;
|
|
103
|
+
/**
|
|
104
|
+
* The session id the handler issued in its cookie, for a host route outside the
|
|
105
|
+
* handler (an OAuth callback) that saves credentials under it. Hosts that pass
|
|
106
|
+
* a `session` resolver use their own id instead.
|
|
107
|
+
*/
|
|
108
|
+
export declare function theoremSessionId(request: Request): string | undefined;
|
|
109
|
+
export declare function createTheoremHandler(options: TheoremHandlerOptions): (request: Request) => Promise<Response>;
|
|
110
|
+
/**
|
|
111
|
+
* Serves a host profile to `<TheoremHost />`: the page calls the profile's tools
|
|
112
|
+
* directly, and every call runs through the kernel's gates and guardrails.
|
|
113
|
+
*/
|
|
114
|
+
export declare function createTheoremHostHandler(options: TheoremHostHandlerOptions): (request: Request) => Promise<Response>;
|