@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ORCHID AI LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,247 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@theoremjs/react`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React projection of the headless interface (`@theoremjs/agents/interface`) — runners, transcript, composer, live stage.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @theoremjs/react @theoremjs/agents zod react react-dom
|
|
9
|
+
# for the chat and live UI (`/ui`, `/live`):
|
|
10
|
+
npm install @astryxdesign/core@0.6.3 @stylexjs/stylex@0.19.0
|
|
11
|
+
# only to build your own theme from the Theorem one (`/ui/theme`):
|
|
12
|
+
npm install @astryxdesign/theme-neutral@0.6.3
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Published to npm only: the package ships built JavaScript, declarations and
|
|
16
|
+
stylesheets. `@theoremjs/agents` is a peer, so the host and the UI share one
|
|
17
|
+
kernel. The Astryx peers are optional: the hooks, client and server entry
|
|
18
|
+
points run without them. The declarations need TypeScript 5.7 or later.
|
|
19
|
+
|
|
20
|
+
`@theoremjs/react/ui` and `@theoremjs/react/live` import their stylesheets
|
|
21
|
+
themselves, so a bundler that handles CSS imports (Vite does) needs no extra
|
|
22
|
+
step. The theme's type is Figtree with a system-font fallback;
|
|
23
|
+
the package does not ship the font, so load it yourself if you want it.
|
|
24
|
+
|
|
25
|
+
The live UI loads its microphone worklet as an asset
|
|
26
|
+
(`new URL('./worklets/mic-capture.js', import.meta.url)`); Vite ships it
|
|
27
|
+
without configuration, and any bundler that resolves that pattern does too.
|
|
28
|
+
|
|
29
|
+
No Svelte. The playground site (`theoremai-frontend`) hosts a thin Vite SPA at `apps/run` that imports this package; the info-site graph stays Svelte and only writes a `PlaygroundRunPayload` handoff.
|
|
30
|
+
|
|
31
|
+
## Imports
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { useTheoremChat, useTheoremInterface } from '@theoremjs/react'; // headless hooks + transport
|
|
35
|
+
import { TheoremChat } from '@theoremjs/react/ui'; // Astryx chat UI
|
|
36
|
+
import { LiveRunner } from '@theoremjs/react/live'; // Astryx voice / video UI
|
|
37
|
+
import { createTheoremHandler } from '@theoremjs/react/server'; // host side
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
A `host` profile runs no model: the page calls its tools directly. Serve it
|
|
41
|
+
with `createTheoremHostHandler({ profile })` and render `<TheoremHost
|
|
42
|
+
endpoint="/api/host" />` (`createHostTransport` and `useTheoremHost` for a UI
|
|
43
|
+
of your own). `GET` describes each allowed tool with its input and output JSON
|
|
44
|
+
Schema, never its endpoint or credentials; `POST /call` `{ name, input }`
|
|
45
|
+
streams the call's events, and `POST /invoke` answers a gate it paused on, as
|
|
46
|
+
in chat. The console draws the form from the input schema and lays the result
|
|
47
|
+
out from its value: figures, charts, tables, images, audio and Markdown, with
|
|
48
|
+
the raw JSON beside them.
|
|
49
|
+
|
|
50
|
+
The playground's run-tab handoff (`savePlaygroundRunPayload`,
|
|
51
|
+
`readPlaygroundRunIdFromUrl`, …) lives in `@theoremjs/playground`; see
|
|
52
|
+
[`playground/README.md`](../playground/README.md).
|
|
53
|
+
|
|
54
|
+
## The wire
|
|
55
|
+
|
|
56
|
+
Turn lines, live envelopes, request bodies, live messages and the profile
|
|
57
|
+
interface `describe` returns are each checked against their schema, both ways:
|
|
58
|
+
|
|
59
|
+
- **Browser → host.** `createTheoremHandler` reads `/turn`, `/invoke` and
|
|
60
|
+
`/steer` bodies with `theoremTurnRequestSchema`, `theoremInvokeRequestSchema`
|
|
61
|
+
and `theoremSteerRequestSchema`; a missing or malformed field is a `request`
|
|
62
|
+
error (400). `/invoke` answers a paused call by its `gateId` with a
|
|
63
|
+
`decision`: `approve` (with `input` when the user edited it, `secret` at a
|
|
64
|
+
sign-in gate) or `deny`; the host settles each one. An answer whose request
|
|
65
|
+
ends before its call settles (the network drops, the host fails) puts the
|
|
66
|
+
call back to wait, to be answered again; once it settled, the session keeps
|
|
67
|
+
the settle (`TheoremSessionState.settled`, pruned with the gates): a second
|
|
68
|
+
answer is refused with `session.gate_expired`, and a walk-away naming the
|
|
69
|
+
call reads its result. A message sent while
|
|
70
|
+
its reply waits walks away in its own `/turn`: `abandon` names the waiting
|
|
71
|
+
calls, its history leaves exactly those open, and the host settles each
|
|
72
|
+
cancelled ahead of the message's reply (`checkWalkAway` and `walkAway` for a
|
|
73
|
+
host with routes of its own). A relay reads the live client's messages with
|
|
74
|
+
`parseLiveClientMessage`. A host with routes of its own reads a body with
|
|
75
|
+
`checkRequest(schema, body, what)`, and answers a paused call with
|
|
76
|
+
`answerGatedCall` (`@theoremjs/agents/kernel`), the rule the handler uses.
|
|
77
|
+
- **Host → browser.** `describe` returns the profile interface as
|
|
78
|
+
`profileInterfaceSchema` names it: tool ids, never a tool's definition, and
|
|
79
|
+
no host functions. The transport and the live client read each line or
|
|
80
|
+
envelope against its kind's schema, and `describe` against
|
|
81
|
+
`profileInterfaceSchema`. A kind the client does not know reaches
|
|
82
|
+
the event handler as `unsupported`, and one that fails its check (not JSON,
|
|
83
|
+
no kind, or a known kind that fails its schema) as `malformed`: a
|
|
84
|
+
`bad_response` naming what broke, never the value. Either way the reply or
|
|
85
|
+
call goes on without it. The chat names a skipped part in the composer and
|
|
86
|
+
the live call in its failure banner, both with the lexicon's
|
|
87
|
+
`session.part_skipped`; a run built on `streamInterfaceTurn` hears of it
|
|
88
|
+
through its `view.skipped`. A `describe` reply that fails its schema is
|
|
89
|
+
`bad_response`.
|
|
90
|
+
|
|
91
|
+
## Local layout
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
Development/
|
|
95
|
+
theoremai/ # kernel + this package
|
|
96
|
+
react/
|
|
97
|
+
theoremai-frontend/ # site; apps/run consumes file:../../../theoremai/react
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Tool credentials
|
|
101
|
+
|
|
102
|
+
Tool credentials live on the server, never in the browser. `createTheoremHandler`
|
|
103
|
+
keeps them in a `credentialStore` keyed by session id (default: process memory;
|
|
104
|
+
use a vault or an encrypted row in production) and loads them into every turn
|
|
105
|
+
and every resumed call.
|
|
106
|
+
|
|
107
|
+
- **Typed keys** — at a bearer or API-key gate, the user types the key into the
|
|
108
|
+
gate card. It goes up once as `secret` on the resume request, and the server
|
|
109
|
+
saves it under the gate's slot; the card keeps no copy and no event carries it.
|
|
110
|
+
The server decides the credential's kind and header from the tool, not the
|
|
111
|
+
request.
|
|
112
|
+
- **OAuth** — pass `authorizationUrl`: begin the flow with `createOAuthPkceFlow`,
|
|
113
|
+
binding it to the `sessionId` you're given, and return its URL; the gate card
|
|
114
|
+
opens it in a popup. Your callback route exchanges the code, saves the
|
|
115
|
+
credential under that session, then its page calls `notifyOAuthComplete(slot)`.
|
|
116
|
+
The card takes that message only from its own popup and origin, and resumes the
|
|
117
|
+
gate with its id alone.
|
|
118
|
+
- **Refresh** — a refreshed OAuth token is saved to the store before the call
|
|
119
|
+
goes on, so a rotated refresh token is never lost.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
const credentialStore = createMemoryCredentialStore();
|
|
123
|
+
const secret = secrets.oauthStateSecret;
|
|
124
|
+
const redirectUri = 'https://app.example/oauth/callback';
|
|
125
|
+
|
|
126
|
+
export const theorem = createTheoremHandler({
|
|
127
|
+
profile,
|
|
128
|
+
provider,
|
|
129
|
+
credentialStore,
|
|
130
|
+
authorizationUrl: async (challenge, { sessionId }) =>
|
|
131
|
+
(
|
|
132
|
+
await createOAuthPkceFlow({
|
|
133
|
+
resourceServerUrl: challenge.resource ?? 'https://api.tracker.example',
|
|
134
|
+
clientId: 'https://app.example/oauth/client.json',
|
|
135
|
+
redirectUri,
|
|
136
|
+
scopes: challenge.requiredScopes,
|
|
137
|
+
signingSecret: secret,
|
|
138
|
+
sessionBinding: sessionId,
|
|
139
|
+
})
|
|
140
|
+
).authorizationUrl,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// The callback route: the handler's session cookie comes with the redirect.
|
|
144
|
+
export async function oauthCallback(request: Request): Promise<Response> {
|
|
145
|
+
const sessionId = theoremSessionId(request);
|
|
146
|
+
if (!sessionId) return new Response(null, { status: 401 });
|
|
147
|
+
const params = new URL(request.url).searchParams;
|
|
148
|
+
const { credential } = await exchangeOAuthPkce({
|
|
149
|
+
code: params.get('code') ?? '',
|
|
150
|
+
state: params.get('state') ?? '',
|
|
151
|
+
iss: params.get('iss') ?? undefined,
|
|
152
|
+
redirectUri,
|
|
153
|
+
signingSecret: secret,
|
|
154
|
+
sessionBinding: sessionId,
|
|
155
|
+
});
|
|
156
|
+
const saved = (await credentialStore.load(sessionId)) ?? {};
|
|
157
|
+
await credentialStore.save(sessionId, { ...saved, tracker: credential });
|
|
158
|
+
// That page runs `notifyOAuthComplete('tracker')` from `@theoremjs/react`.
|
|
159
|
+
return Response.redirect(new URL('/oauth/done?slot=tracker', request.url), 303);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
A host with its own `session` resolver passes its own session id instead of
|
|
164
|
+
`theoremSessionId`. For voice, the relay you host receives a typed key as `secret`
|
|
165
|
+
on the `executeTool` message: save it with `credentialFromTypedSecret` (from
|
|
166
|
+
`@theoremjs/agents/kernel`) under the gate's slot, and pass a
|
|
167
|
+
`ToolCredentialSource` over your store as `credentials` on every `executeTool`.
|
|
168
|
+
|
|
169
|
+
## Composer pending intents
|
|
170
|
+
|
|
171
|
+
`src/interface/` owns stash / queue / steer list ops and the action matrix.
|
|
172
|
+
This package wires AbortSignal, the pending bar, and playground turn/steer HTTP.
|
|
173
|
+
|
|
174
|
+
| Phase | Empty composer | Filled composer |
|
|
175
|
+
| --- | --- | --- |
|
|
176
|
+
| idle | disabled | Send (+ Stash menu) |
|
|
177
|
+
| streaming | Stop | Queue (+ Steer / Send now / Stash) |
|
|
178
|
+
| gated | disabled | Queue (+ Send now / Stash; no Steer) |
|
|
179
|
+
|
|
180
|
+
Enter matches the primary action. No keyboard shortcuts for stash/steer.
|
|
181
|
+
|
|
182
|
+
Send now while gated walks away from every waiting gate in the message's own request
|
|
183
|
+
(`walkAway` on `streamInterfaceDraftTurn`): the host settles each call cancelled, the
|
|
184
|
+
paused reply commits, and the message's reply follows in the same stream. The message
|
|
185
|
+
leaves the composer only once it posts; if the request fails first, the reply still waits.
|
|
186
|
+
An answer at a gate that fails before its call settles leaves the gate to answer again,
|
|
187
|
+
the failure in the composer; one that fails after keeps the call's result. The gate cards
|
|
188
|
+
show the answer on its way from their owner (`useTheoremChat().answering`, passed as
|
|
189
|
+
`ChatTranscript`'s `answering`, `ApprovalCard`'s `decided`, `AuthChallengeCard`'s
|
|
190
|
+
`submitted`), so a failed answer brings the gate's actions back. Steer POSTs use the Cache API
|
|
191
|
+
on Cloudflare (process Map locally) so mid-turn injects work across isolates.
|
|
192
|
+
Live sessions key the same inbox by `sessionId` from relay `ready`.
|
|
193
|
+
|
|
194
|
+
A chat can be kept and resumed: `onChatChange` (or `useTheoremChat`'s `onChange`) reports the
|
|
195
|
+
`{ blocks, session }` each time the conversation comes to rest (a turn finished, a message
|
|
196
|
+
added or removed; never while a reply streams or waits on a gate), and `initialChat` (`initial`)
|
|
197
|
+
resumes from one, the session carrying what the next turn is sent with. `chatRef` (`sendText`
|
|
198
|
+
on the hook) sends a message as the composer would and resolves with the blocks the turn added
|
|
199
|
+
once its reply is done, or `null` while a reply streams or waits on a gate.
|
|
200
|
+
|
|
201
|
+
Pending rows show attachment / voice previews, text, **Queue** (stash → queue),
|
|
202
|
+
and **Send now**. Clicking the text restores the full draft (text + files + voice)
|
|
203
|
+
into the composer; if the composer already had a payload, that payload is re-stashed.
|
|
204
|
+
|
|
205
|
+
## Wording
|
|
206
|
+
|
|
207
|
+
The headless layer (hooks, client, components, server handler) writes no
|
|
208
|
+
English. It hands the builder kinds, codes, and states:
|
|
209
|
+
|
|
210
|
+
- Failures are `ClientFailure` (`{ error, errorKind, errorInternal? }`). `error`
|
|
211
|
+
is the profile's wording (`iface.lexicon`, resolved on the host); show it to
|
|
212
|
+
the user. `errorKind` and `errorInternal` are for the builder.
|
|
213
|
+
- Attachment problems are `AttachmentValidationIssue`s; word one with
|
|
214
|
+
`attachmentIssueText(issue, iface.lexicon)` from `@theoremjs/agents`.
|
|
215
|
+
- A Live call the provider ended after warning it would is not a failure:
|
|
216
|
+
`LiveSessionClient`'s `onSessionEnded(session)` gives `session.message`, the
|
|
217
|
+
profile's `live.session_ended` wording, and `session.ended` (close code,
|
|
218
|
+
timing, the code's kind) for the builder.
|
|
219
|
+
- Chrome is semantic: `liveState`, `workStatus`, drawer `parts`, hint `id`.
|
|
220
|
+
|
|
221
|
+
`@theoremjs/react/ui` is the default UI. Every line it shows is an Astryx i18n
|
|
222
|
+
message: Theorem's under `@theorem.*` keys (`THEOREM_UI_CATALOG`, each with a
|
|
223
|
+
description and the ICU values it takes), Astryx's own under `@astryx.*`. The
|
|
224
|
+
builder owns all of them; `labels` replaces any line, per locale:
|
|
225
|
+
|
|
226
|
+
```tsx
|
|
227
|
+
<TheoremChat
|
|
228
|
+
labels={{
|
|
229
|
+
en: {
|
|
230
|
+
'@theorem.chat.greeting': 'What shall we plan?',
|
|
231
|
+
'@theorem.composer.placeholder': 'Write to @{handle}',
|
|
232
|
+
'@astryx.chatSendButton.send': 'Go',
|
|
233
|
+
},
|
|
234
|
+
de: { '@theorem.chat.greeting': 'Woran arbeiten wir?' },
|
|
235
|
+
}}
|
|
236
|
+
/>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
`LiveRunner` takes the same prop. The locale is the host's Astryx
|
|
240
|
+
`InternationalizationProvider` locale (`en` without one); a host's own Astryx
|
|
241
|
+
`messages` and `overrides` also apply, and win over the defaults. Labels are
|
|
242
|
+
checked on mount: an unknown `@theorem.*` key, a message that is not valid ICU,
|
|
243
|
+
a value the line is not given, or a key outside `@theorem.*` / `@astryx.*`
|
|
244
|
+
throws, naming the locale and key. Label values render as text, never HTML.
|
|
245
|
+
|
|
246
|
+
A builder with their own UI replaces all of it.
|
|
247
|
+
`scripts/docs-truth/copy-lint.mjs` keeps prose out of the headless directories.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Hover-card geometry for composer attachment pills (model-sculpt agent_chat parity). */
|
|
2
|
+
export type AttachPreviewStyle = {
|
|
3
|
+
left: number;
|
|
4
|
+
top?: number;
|
|
5
|
+
bottom?: number;
|
|
6
|
+
};
|
|
7
|
+
export declare function formatAttachmentSize(bytes?: number): string;
|
|
8
|
+
export declare function resolveAttachPreviewStyle(anchor: Pick<DOMRect, 'left' | 'top' | 'bottom'>, viewport?: {
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
}): AttachPreviewStyle;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Hover-card geometry for composer attachment pills (model-sculpt agent_chat parity). */
|
|
2
|
+
const ATTACH_PREVIEW_WIDTH_PX = 240;
|
|
3
|
+
const ATTACH_PREVIEW_OFFSET_PX = 6;
|
|
4
|
+
const ATTACH_PREVIEW_VIEWPORT_PAD_PX = 8;
|
|
5
|
+
/** Prefer anchoring above the chip when this much space exists. */
|
|
6
|
+
const ATTACH_PREVIEW_ABOVE_THRESHOLD_PX = 190;
|
|
7
|
+
export function formatAttachmentSize(bytes) {
|
|
8
|
+
if (!bytes || bytes <= 0)
|
|
9
|
+
return '';
|
|
10
|
+
if (bytes < 1024)
|
|
11
|
+
return `${String(bytes)} B`;
|
|
12
|
+
if (bytes < 1024 * 1024)
|
|
13
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
14
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
15
|
+
}
|
|
16
|
+
function clampAttachPreviewLeft(anchorLeft, viewportWidth) {
|
|
17
|
+
const maxLeft = Math.max(ATTACH_PREVIEW_VIEWPORT_PAD_PX, viewportWidth - ATTACH_PREVIEW_WIDTH_PX - ATTACH_PREVIEW_VIEWPORT_PAD_PX);
|
|
18
|
+
return Math.min(Math.max(ATTACH_PREVIEW_VIEWPORT_PAD_PX, anchorLeft), maxLeft);
|
|
19
|
+
}
|
|
20
|
+
export function resolveAttachPreviewStyle(anchor, viewport = {
|
|
21
|
+
width: globalThis.innerWidth ?? 0,
|
|
22
|
+
height: globalThis.innerHeight ?? 0,
|
|
23
|
+
}) {
|
|
24
|
+
const left = clampAttachPreviewLeft(anchor.left, viewport.width);
|
|
25
|
+
const hasRoomAbove = anchor.top > ATTACH_PREVIEW_ABOVE_THRESHOLD_PX;
|
|
26
|
+
if (hasRoomAbove) {
|
|
27
|
+
return {
|
|
28
|
+
left,
|
|
29
|
+
bottom: Math.max(ATTACH_PREVIEW_VIEWPORT_PAD_PX, viewport.height - anchor.top + ATTACH_PREVIEW_OFFSET_PX),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
left,
|
|
34
|
+
top: Math.min(viewport.height - ATTACH_PREVIEW_VIEWPORT_PAD_PX, anchor.bottom + ATTACH_PREVIEW_OFFSET_PX),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Shared mic RMS → waveform level scaling for live and turn-composer paths. */
|
|
2
|
+
export declare const INPUT_LEVEL_GAIN = 4;
|
|
3
|
+
export declare function float32Rms(samples: Float32Array): number;
|
|
4
|
+
export declare function float32RmsToLevel(samples: Float32Array): number;
|
|
5
|
+
export declare function timeDomainBytesToLevel(samples: Uint8Array<ArrayBufferLike>): number;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Shared mic RMS → waveform level scaling for live and turn-composer paths. */
|
|
2
|
+
export const INPUT_LEVEL_GAIN = 4;
|
|
3
|
+
export function float32Rms(samples) {
|
|
4
|
+
let sum = 0;
|
|
5
|
+
for (const sample of samples) {
|
|
6
|
+
sum += sample * sample;
|
|
7
|
+
}
|
|
8
|
+
if (samples.length === 0)
|
|
9
|
+
return 0;
|
|
10
|
+
return Math.sqrt(sum / samples.length);
|
|
11
|
+
}
|
|
12
|
+
export function float32RmsToLevel(samples) {
|
|
13
|
+
return Math.min(1, float32Rms(samples) * INPUT_LEVEL_GAIN);
|
|
14
|
+
}
|
|
15
|
+
export function timeDomainBytesToLevel(samples) {
|
|
16
|
+
let sum = 0;
|
|
17
|
+
for (const sample of samples) {
|
|
18
|
+
const normalized = (sample - 128) / 128;
|
|
19
|
+
sum += normalized * normalized;
|
|
20
|
+
}
|
|
21
|
+
if (samples.length === 0)
|
|
22
|
+
return 0;
|
|
23
|
+
return Math.min(1, Math.sqrt(sum / samples.length) * INPUT_LEVEL_GAIN);
|
|
24
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stage file picks against profile `maxFiles` (shared with voice). Excess
|
|
3
|
+
* incoming files are dropped; the limit hit is an issue the caller words
|
|
4
|
+
* (`attachmentIssueText`).
|
|
5
|
+
*/
|
|
6
|
+
import type { AttachmentValidationIssue } from '@theoremjs/agents/interface';
|
|
7
|
+
export type StageComposerFilesArgs = {
|
|
8
|
+
existing: readonly File[];
|
|
9
|
+
incoming: readonly File[];
|
|
10
|
+
/** Profile `inputs.maxFiles`. When omitted, no cap. */
|
|
11
|
+
maxFiles?: number;
|
|
12
|
+
/** Staged voice notes that share the same maxFiles budget. */
|
|
13
|
+
voiceCount?: number;
|
|
14
|
+
};
|
|
15
|
+
export type StageComposerFilesResult = {
|
|
16
|
+
files: File[];
|
|
17
|
+
dropped: number;
|
|
18
|
+
/** `too_many_files` when the cap dropped a file. */
|
|
19
|
+
issues: AttachmentValidationIssue[];
|
|
20
|
+
};
|
|
21
|
+
/** Append `incoming` to `existing`, dropping overflow past maxFiles − voice. */
|
|
22
|
+
export declare function stageComposerFiles(args: StageComposerFilesArgs): StageComposerFilesResult;
|
|
23
|
+
/**
|
|
24
|
+
* Whether a new voice note fits under maxFiles.
|
|
25
|
+
* Recording clears any prior staged voice first, so only `fileCount` matters.
|
|
26
|
+
*/
|
|
27
|
+
export declare function canStageVoice(args: {
|
|
28
|
+
fileCount: number;
|
|
29
|
+
maxFiles?: number;
|
|
30
|
+
}): boolean;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stage file picks against profile `maxFiles` (shared with voice). Excess
|
|
3
|
+
* incoming files are dropped; the limit hit is an issue the caller words
|
|
4
|
+
* (`attachmentIssueText`).
|
|
5
|
+
*/
|
|
6
|
+
/** Append `incoming` to `existing`, dropping overflow past maxFiles − voice. */
|
|
7
|
+
export function stageComposerFiles(args) {
|
|
8
|
+
const { existing, incoming, maxFiles } = args;
|
|
9
|
+
const voiceCount = Math.max(0, args.voiceCount ?? 0);
|
|
10
|
+
if (maxFiles === undefined || !Number.isFinite(maxFiles) || maxFiles < 0) {
|
|
11
|
+
return { files: [...existing, ...incoming], dropped: 0, issues: [] };
|
|
12
|
+
}
|
|
13
|
+
const room = Math.max(0, maxFiles - voiceCount - existing.length);
|
|
14
|
+
if (incoming.length <= room) {
|
|
15
|
+
return { files: [...existing, ...incoming], dropped: 0, issues: [] };
|
|
16
|
+
}
|
|
17
|
+
const kept = room === 0 ? [] : incoming.slice(0, room);
|
|
18
|
+
return {
|
|
19
|
+
files: [...existing, ...kept],
|
|
20
|
+
dropped: incoming.length - kept.length,
|
|
21
|
+
issues: [{ code: 'too_many_files', params: { maxFiles } }],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Whether a new voice note fits under maxFiles.
|
|
26
|
+
* Recording clears any prior staged voice first, so only `fileCount` matters.
|
|
27
|
+
*/
|
|
28
|
+
export function canStageVoice(args) {
|
|
29
|
+
const maxFiles = args.maxFiles;
|
|
30
|
+
if (maxFiles === undefined || !Number.isFinite(maxFiles))
|
|
31
|
+
return true;
|
|
32
|
+
return args.fileCount < maxFiles;
|
|
33
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ComposerPendingMessage } from '@theoremjs/agents/interface';
|
|
2
|
+
/** What waits in the composer, by kind; the UI words each kind. */
|
|
3
|
+
export type ComposerDrawerKind = 'steer' | 'queue' | 'stash' | 'attached';
|
|
4
|
+
/** Collapsed-drawer header: the total, then each waiting kind's count, in display order. */
|
|
5
|
+
export type ComposerDrawerSummary = {
|
|
6
|
+
count: number;
|
|
7
|
+
parts: {
|
|
8
|
+
kind: ComposerDrawerKind;
|
|
9
|
+
n: number;
|
|
10
|
+
}[];
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* What's waiting in the composer, by kind: steering, queued, stashed, attached
|
|
14
|
+
* (files and voice notes). Null when nothing waits.
|
|
15
|
+
*/
|
|
16
|
+
export declare function composerDrawerSummary(args: {
|
|
17
|
+
pendingMessages: readonly Pick<ComposerPendingMessage, 'kind'>[];
|
|
18
|
+
attachmentCount: number;
|
|
19
|
+
}): ComposerDrawerSummary | null;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What's waiting in the composer, by kind: steering, queued, stashed, attached
|
|
3
|
+
* (files and voice notes). Null when nothing waits.
|
|
4
|
+
*/
|
|
5
|
+
export function composerDrawerSummary(args) {
|
|
6
|
+
const countOf = (kind) => args.pendingMessages.filter((message) => message.kind === kind).length;
|
|
7
|
+
const parts = [
|
|
8
|
+
{ kind: 'steer', n: countOf('steer') },
|
|
9
|
+
{ kind: 'queue', n: countOf('queue') },
|
|
10
|
+
{ kind: 'stash', n: countOf('stash') },
|
|
11
|
+
{ kind: 'attached', n: args.attachmentCount },
|
|
12
|
+
].filter((part) => part.n > 0);
|
|
13
|
+
const count = parts.reduce((sum, part) => sum + part.n, 0);
|
|
14
|
+
return count === 0 ? null : { count, parts };
|
|
15
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composer hints — ephemeral guidance about the current composer interaction
|
|
3
|
+
* (not pending-message state). Mirrors seance's `stash-selected-draft` hint.
|
|
4
|
+
*/
|
|
5
|
+
/** Stash shortcut in Astryx `Kbd` notation (`mod` is ⌘ on macOS, Ctrl elsewhere). */
|
|
6
|
+
export declare const STASH_SHORTCUT = "mod+shift+s";
|
|
7
|
+
/** Which hint applies, and its shortcut; the UI words it. */
|
|
8
|
+
export type ComposerHint = {
|
|
9
|
+
id: 'stash-selected-draft';
|
|
10
|
+
shortcut: string;
|
|
11
|
+
};
|
|
12
|
+
/** Whether a keydown is the stash shortcut (⌘⇧S / Ctrl+Shift+S). */
|
|
13
|
+
export declare function isStashShortcut(event: {
|
|
14
|
+
code: string;
|
|
15
|
+
metaKey: boolean;
|
|
16
|
+
ctrlKey: boolean;
|
|
17
|
+
shiftKey: boolean;
|
|
18
|
+
altKey: boolean;
|
|
19
|
+
}): boolean;
|
|
20
|
+
/** The stash hint — shown while the whole draft is selected in the focused input and it can be stashed. */
|
|
21
|
+
export declare function resolveComposerHint(args: {
|
|
22
|
+
draftText: string;
|
|
23
|
+
selectedText: string;
|
|
24
|
+
canStash: boolean;
|
|
25
|
+
}): ComposerHint | null;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composer hints — ephemeral guidance about the current composer interaction
|
|
3
|
+
* (not pending-message state). Mirrors seance's `stash-selected-draft` hint.
|
|
4
|
+
*/
|
|
5
|
+
/** Stash shortcut in Astryx `Kbd` notation (`mod` is ⌘ on macOS, Ctrl elsewhere). */
|
|
6
|
+
export const STASH_SHORTCUT = 'mod+shift+s';
|
|
7
|
+
/** Whether a keydown is the stash shortcut (⌘⇧S / Ctrl+Shift+S). */
|
|
8
|
+
export function isStashShortcut(event) {
|
|
9
|
+
return event.code === 'KeyS' && event.shiftKey && !event.altKey && (event.metaKey || event.ctrlKey);
|
|
10
|
+
}
|
|
11
|
+
/** The stash hint — shown while the whole draft is selected in the focused input and it can be stashed. */
|
|
12
|
+
export function resolveComposerHint(args) {
|
|
13
|
+
const draft = args.draftText.trim();
|
|
14
|
+
if (!args.canStash || !draft || args.selectedText.trim() !== draft)
|
|
15
|
+
return null;
|
|
16
|
+
return {
|
|
17
|
+
id: 'stash-selected-draft',
|
|
18
|
+
shortcut: STASH_SHORTCUT,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ComposerMenuAction, type ComposerPrimaryAction, type ComposerProfileInterface, type ComposerRunPhase } from '@theoremjs/agents/interface';
|
|
2
|
+
export type ComposerActionState = {
|
|
3
|
+
primary: ComposerPrimaryAction;
|
|
4
|
+
menuActions: ComposerMenuAction[];
|
|
5
|
+
/** Send is off while recording, with nothing to act on, or with an empty draft to send or queue. */
|
|
6
|
+
primaryDisabled: boolean;
|
|
7
|
+
};
|
|
8
|
+
/** The composer's primary button and send menu for the current draft and run phase. */
|
|
9
|
+
export declare function composerActionState(args: {
|
|
10
|
+
iface: ComposerProfileInterface;
|
|
11
|
+
phase: ComposerRunPhase;
|
|
12
|
+
draftText: string;
|
|
13
|
+
pendingFiles: readonly File[];
|
|
14
|
+
pendingVoice: readonly File[];
|
|
15
|
+
recording: boolean;
|
|
16
|
+
}): ComposerActionState;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { resolveComposerMenuActions, resolveComposerPrimary, userDraftHasPayload, } from '@theoremjs/agents/interface';
|
|
2
|
+
function fileSpecs(files) {
|
|
3
|
+
return files.map((file) => ({
|
|
4
|
+
name: file.name,
|
|
5
|
+
mimeType: file.type || 'application/octet-stream',
|
|
6
|
+
sizeBytes: file.size,
|
|
7
|
+
}));
|
|
8
|
+
}
|
|
9
|
+
/** The composer's primary button and send menu for the current draft and run phase. */
|
|
10
|
+
export function composerActionState(args) {
|
|
11
|
+
const allowSteering = 'allowSteering' in args.iface && Boolean(args.iface.allowSteering);
|
|
12
|
+
const hasPayload = userDraftHasPayload({
|
|
13
|
+
...(args.draftText.trim() ? { text: args.draftText } : {}),
|
|
14
|
+
...(args.pendingFiles.length ? { attachments: fileSpecs(args.pendingFiles) } : {}),
|
|
15
|
+
...(args.pendingVoice.length ? { voice: fileSpecs(args.pendingVoice) } : {}),
|
|
16
|
+
});
|
|
17
|
+
const context = { phase: args.phase, hasPayload, allowSteering };
|
|
18
|
+
const primary = resolveComposerPrimary(context);
|
|
19
|
+
const needsPayload = primary === 'send' || primary === 'queue';
|
|
20
|
+
return {
|
|
21
|
+
primary,
|
|
22
|
+
menuActions: resolveComposerMenuActions(context),
|
|
23
|
+
primaryDisabled: args.recording || primary === 'none' || (needsPayload && !hasPayload),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The browser side of a decision profile: what the host lets the page see of
|
|
3
|
+
* it, and the one call that asks its questions about some state.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import { type DecisionJson, type DecisionProfile, type DecisionQuestion, type DecisionResult, type TraceRecord } from '@theoremjs/agents';
|
|
8
|
+
import type { TraceFeed } from './trace-feed.ts';
|
|
9
|
+
import { type HttpOptions } from './transport.ts';
|
|
10
|
+
/** One question as the page sees it: its answer type and the names of its options or levels. Instructions stay on the host. */
|
|
11
|
+
export type DecisionQuestionView = {
|
|
12
|
+
id: string;
|
|
13
|
+
type: DecisionQuestion['type'];
|
|
14
|
+
/** A choice's labels, a score's levels from 0 up; empty for a number. */
|
|
15
|
+
labels: string[];
|
|
16
|
+
};
|
|
17
|
+
/** What a decision profile shows the page. */
|
|
18
|
+
export type DecisionInterface = {
|
|
19
|
+
type: 'decision';
|
|
20
|
+
identity: {
|
|
21
|
+
handle: string;
|
|
22
|
+
};
|
|
23
|
+
contract: string;
|
|
24
|
+
maxStateBytes?: number;
|
|
25
|
+
questions: DecisionQuestionView[];
|
|
26
|
+
observability?: {
|
|
27
|
+
record: boolean;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/** The page's view of a decision profile and the questions its host asks. */
|
|
31
|
+
export declare function decisionInterface(profile: Pick<DecisionProfile, 'identity' | 'decision' | 'inputs' | 'observability'>, questions: Record<string, DecisionQuestion>): DecisionInterface;
|
|
32
|
+
/** A decision's reply: the answers, and the trace records a host that delivers them sent back. */
|
|
33
|
+
export type DecisionReply = {
|
|
34
|
+
result: DecisionResult;
|
|
35
|
+
traces?: TraceRecord[];
|
|
36
|
+
};
|
|
37
|
+
export interface DecisionTransport {
|
|
38
|
+
describe(signal?: AbortSignal): Promise<DecisionInterface>;
|
|
39
|
+
decide(state: Exclude<DecisionJson, null>, signal?: AbortSignal): Promise<DecisionReply>;
|
|
40
|
+
/** Where the host's trace records land, for the inspector. */
|
|
41
|
+
traces?: TraceFeed;
|
|
42
|
+
}
|
|
43
|
+
/** A host's decide reply, checked: the answers, and any trace records it delivers. */
|
|
44
|
+
export declare function readDecisionReply(raw: unknown): DecisionReply;
|
|
45
|
+
/** Transport for a host mounted with `createTheoremDecisionHandler`. Default endpoint `/api/decision`. */
|
|
46
|
+
export declare function createDecisionTransport(options?: HttpOptions & {
|
|
47
|
+
endpoint?: string;
|
|
48
|
+
}): DecisionTransport;
|