@theoremjs/react 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +728 -202
- package/client/composer-hints.js +1 -1
- package/client/decision-images.d.ts +76 -0
- package/client/decision-images.js +139 -0
- package/client/decision-transport.d.ts +26 -4
- package/client/decision-transport.js +79 -6
- package/client/embedding-input.d.ts +84 -0
- package/client/embedding-input.js +194 -0
- package/client/embedding-space.d.ts +82 -0
- package/client/embedding-space.js +389 -0
- package/client/embedding-transport.d.ts +40 -0
- package/client/embedding-transport.js +71 -0
- package/client/failure.d.ts +7 -10
- package/client/failure.js +9 -9
- package/client/host-call.d.ts +1 -1
- package/client/host-call.js +7 -2
- package/client/host-transport.d.ts +2 -0
- package/client/host-transport.js +3 -2
- package/client/index.d.ts +19 -11
- package/client/index.js +14 -10
- package/client/ink-waveform.d.ts +1 -1
- package/client/ink-waveform.js +1 -2
- package/client/live/apply-live-turn-tool-event.js +1 -1
- package/client/live/live-captions.d.ts +30 -0
- package/client/live/live-captions.js +105 -1
- package/client/live/live-mic-forward.d.ts +1 -0
- package/client/live/live-mic-forward.js +11 -2
- package/client/live/live-page-tool.d.ts +34 -0
- package/client/live/live-page-tool.js +11 -0
- package/client/live/live-state.d.ts +4 -2
- package/client/live/live-state.js +2 -0
- package/client/live/live-video.js +1 -1
- package/client/live/run-live-tool-call.d.ts +7 -2
- package/client/live/run-live-tool-call.js +48 -10
- package/client/live/use-live-caption-log.d.ts +17 -0
- package/client/live/use-live-caption-log.js +51 -0
- package/client/live-client.d.ts +52 -4
- package/client/live-client.js +250 -59
- package/client/live-messages.d.ts +51 -13
- package/client/live-messages.js +42 -9
- package/client/page-dispatch.d.ts +15 -0
- package/client/page-dispatch.js +63 -0
- package/client/run-commit.d.ts +16 -43
- package/client/run-commit.js +26 -101
- package/client/run-session.d.ts +40 -41
- package/client/run-session.js +104 -112
- package/client/schema-fields.js +27 -6
- package/client/shaped-data.js +37 -8
- package/client/source-citations.js +1 -1
- package/client/space-geometry.d.ts +39 -0
- package/client/space-geometry.js +183 -0
- package/client/state-fields.js +1 -1
- package/client/tool-result.js +30 -12
- package/client/tool-resume.d.ts +18 -1
- package/client/tool-resume.js +4 -1
- package/client/trace-feed.d.ts +1 -1
- package/client/trace-story.d.ts +2 -2
- package/client/trace-story.js +43 -19
- package/client/trace-view.d.ts +2 -2
- package/client/trace-view.js +56 -16
- package/client/transcript-groups.d.ts +28 -2
- package/client/transcript-groups.js +63 -13
- package/client/transcript-schema.d.ts +10 -0
- package/client/transcript-schema.js +120 -0
- package/client/transport.d.ts +114 -26
- package/client/transport.js +106 -32
- package/client/turn-client.d.ts +17 -8
- package/client/turn-client.js +26 -31
- package/client/voice-recorder.d.ts +1 -1
- package/client/voice-recorder.js +2 -5
- package/client/wire-line.js +5 -5
- package/client/worklets/mic-capture.js +9 -9
- package/components/InkWaveform.d.ts +12 -1
- package/components/InkWaveform.js +65 -42
- package/components/live/use-live-runner-controls.d.ts +3 -4
- package/components/live/use-live-runner-controls.js +6 -10
- package/components/live/use-live-runner-model.d.ts +10 -6
- package/components/live/use-live-runner-model.js +29 -14
- package/components/live/use-live-runner-ui.d.ts +10 -8
- package/components/live/use-live-runner-ui.js +19 -11
- package/components/live/use-live-session-client.d.ts +4 -4
- package/components/live/use-live-session-client.js +11 -8
- package/components/use-composer-voice.d.ts +4 -4
- package/components/use-composer-voice.js +8 -8
- package/components/use-editor-selection.js +4 -1
- package/components/use-voice-playback.d.ts +2 -1
- package/components/use-voice-playback.js +4 -4
- package/hooks/use-described.d.ts +5 -1
- package/hooks/use-described.js +11 -9
- package/hooks/use-embedding-index.d.ts +27 -0
- package/hooks/use-embedding-index.js +43 -0
- package/hooks/use-one-call.d.ts +31 -0
- package/hooks/use-one-call.js +76 -0
- package/hooks/use-page-tool-warnings.d.ts +6 -0
- package/hooks/use-page-tool-warnings.js +25 -0
- package/hooks/use-second-ticker.d.ts +2 -0
- package/hooks/use-second-ticker.js +13 -0
- package/hooks/use-theorem-chat-actions.d.ts +25 -21
- package/hooks/use-theorem-chat-actions.js +67 -45
- package/hooks/use-theorem-chat-state.d.ts +6 -6
- package/hooks/use-theorem-chat-state.js +13 -9
- package/hooks/use-theorem-chat.d.ts +48 -14
- package/hooks/use-theorem-chat.js +329 -141
- package/hooks/use-theorem-decision.d.ts +5 -10
- package/hooks/use-theorem-decision.js +4 -67
- package/hooks/use-theorem-embedding.d.ts +18 -0
- package/hooks/use-theorem-embedding.js +12 -0
- package/hooks/use-theorem-host.d.ts +5 -4
- package/hooks/use-theorem-host.js +26 -8
- package/hooks/use-theorem-interface.d.ts +6 -3
- package/hooks/use-theorem-interface.js +12 -22
- package/index.d.ts +6 -3
- package/index.js +5 -2
- package/live.d.ts +1 -0
- package/live.js +1 -1
- package/package.json +6 -5
- package/server/checkpoint-ownership.d.ts +6 -0
- package/server/checkpoint-ownership.js +42 -0
- package/server/conversation-store.d.ts +92 -0
- package/server/conversation-store.js +131 -0
- package/server/conversation.d.ts +193 -0
- package/server/conversation.js +552 -0
- package/server/decision-handler.d.ts +12 -4
- package/server/decision-handler.js +29 -38
- package/server/embedding-handler.d.ts +33 -0
- package/server/embedding-handler.js +45 -0
- package/server/handler.d.ts +58 -20
- package/server/handler.js +441 -238
- package/server/mod.d.ts +15 -6
- package/server/mod.js +13 -5
- package/server/page-dispatch.d.ts +22 -0
- package/server/page-dispatch.js +127 -0
- package/server/request-body.d.ts +2 -0
- package/server/request-body.js +42 -0
- package/server/request-check.d.ts +6 -1
- package/server/request-check.js +27 -12
- package/server/session-authority.d.ts +8 -0
- package/server/session-authority.js +78 -0
- package/server/session-boundary.d.ts +26 -0
- package/server/session-boundary.js +64 -0
- package/server/session-identity.d.ts +16 -0
- package/server/session-identity.js +41 -0
- package/server/session-limits.d.ts +12 -0
- package/server/session-limits.js +59 -0
- package/server/session-store.d.ts +69 -20
- package/server/session-store.js +220 -5
- package/server/single-action.d.ts +21 -0
- package/server/single-action.js +30 -0
- package/server/steer-inbox.js +1 -1
- package/server/tool-operations.d.ts +6 -0
- package/server/tool-operations.js +132 -0
- package/server/turn-input.d.ts +10 -0
- package/server/turn-input.js +29 -0
- package/styles/ink-controls.css +78 -78
- package/ui/CardWall.d.ts +10 -0
- package/ui/CardWall.js +61 -0
- package/ui/ChatComposerBar.d.ts +28 -5
- package/ui/ChatComposerBar.js +85 -30
- package/ui/ChatTranscript.d.ts +14 -1
- package/ui/ChatTranscript.js +110 -57
- package/ui/ConsoleFrame.d.ts +61 -4
- package/ui/ConsoleFrame.js +109 -9
- package/ui/DecisionAnswers.d.ts +40 -2
- package/ui/DecisionAnswers.js +166 -35
- package/ui/DecisionImages.d.ts +16 -0
- package/ui/DecisionImages.js +46 -0
- package/ui/EmbeddingDocuments.d.ts +78 -0
- package/ui/EmbeddingDocuments.js +278 -0
- package/ui/EmbeddingQuery.d.ts +27 -0
- package/ui/EmbeddingQuery.js +95 -0
- package/ui/EmbeddingResults.d.ts +41 -0
- package/ui/EmbeddingResults.js +165 -0
- package/ui/EmbeddingSpace.d.ts +26 -0
- package/ui/EmbeddingSpace.js +106 -0
- package/ui/LiveCaptionsPanel.d.ts +22 -0
- package/ui/LiveCaptionsPanel.js +27 -0
- package/ui/LiveRunner.d.ts +6 -4
- package/ui/LiveRunner.js +42 -55
- package/ui/PaneState.d.ts +33 -0
- package/ui/PaneState.js +50 -0
- package/ui/SchemaFields.d.ts +1 -1
- package/ui/SchemaFields.js +54 -26
- package/ui/ShapedData.js +73 -28
- package/ui/SidePanel.d.ts +74 -5
- package/ui/SidePanel.js +82 -9
- package/ui/StateFields.d.ts +10 -2
- package/ui/StateFields.js +56 -13
- package/ui/TheoremChat.d.ts +65 -14
- package/ui/TheoremChat.js +66 -78
- package/ui/TheoremDecision.d.ts +12 -20
- package/ui/TheoremDecision.js +78 -35
- package/ui/TheoremEmbedding.d.ts +27 -0
- package/ui/TheoremEmbedding.js +122 -0
- package/ui/TheoremHost.d.ts +22 -5
- package/ui/TheoremHost.js +127 -37
- package/ui/ToolGateCard.d.ts +5 -1
- package/ui/ToolGateCard.js +30 -23
- package/ui/ToolResult.js +12 -10
- package/ui/ToolResultCharts.js +29 -12
- package/ui/TraceCharts.d.ts +2 -2
- package/ui/TraceCharts.js +48 -17
- package/ui/TraceGuardrails.d.ts +3 -2
- package/ui/TraceGuardrails.js +24 -6
- package/ui/TraceInspector.d.ts +11 -1
- package/ui/TraceInspector.js +45 -28
- package/ui/TraceInspectorPanel.d.ts +31 -9
- package/ui/TraceInspectorPanel.js +77 -39
- package/ui/TraceOverview.d.ts +7 -3
- package/ui/TraceOverview.js +44 -14
- package/ui/TraceSpanDetail.js +24 -22
- package/ui/TraceStory.js +9 -8
- package/ui/TraceValues.js +19 -9
- package/ui/TraceWaterfall.js +12 -9
- package/ui/VoiceNote.js +4 -4
- package/ui/approval-heading.d.ts +4 -0
- package/ui/approval-heading.js +9 -0
- package/ui/arrive.d.ts +18 -0
- package/ui/arrive.js +46 -0
- package/ui/built/theme.css +86 -68
- package/ui/built/theme.css.d.ts +9 -0
- package/ui/built/theorem.d.ts +3 -3
- package/ui/built/theorem.js +34 -5
- package/ui/built/theorem.variants.d.ts +8 -2
- package/ui/code-language.d.ts +12 -0
- package/ui/code-language.js +50 -0
- package/ui/disclosure-motion.js +53 -9
- package/ui/embedding-space-gl.d.ts +37 -0
- package/ui/embedding-space-gl.js +663 -0
- package/ui/embedding-space-view.d.ts +8 -0
- package/ui/embedding-space-view.js +8 -0
- package/ui/glide.d.ts +7 -0
- package/ui/glide.js +44 -0
- package/ui/icons.js +2 -1
- package/ui/index.d.ts +20 -9
- package/ui/index.js +20 -10
- package/ui/labels-provider.js +5 -2
- package/ui/labels.d.ts +374 -85
- package/ui/labels.js +1028 -188
- package/ui/markdown-plugins.d.ts +9 -0
- package/ui/markdown-plugins.js +13 -0
- package/ui/mcp-icon.d.ts +6 -0
- package/ui/mcp-icon.js +8 -0
- package/ui/row-keys.d.ts +19 -0
- package/ui/row-keys.js +33 -0
- package/ui/theme.d.ts +1 -1
- package/ui/theme.js +2 -2
- package/ui/theorem-theme.js +32 -10
- package/ui/trace-palette.d.ts +2 -16
- package/ui/trace-palette.js +2 -16
- package/ui/trace-patterns.d.ts +11 -3
- package/ui/trace-patterns.js +16 -3
- package/ui/transcript-copy-text.js +1 -0
- package/ui/wall-columns.d.ts +8 -0
- package/ui/wall-columns.js +19 -0
- package/client/walk-away.d.ts +0 -34
- package/client/walk-away.js +0 -48
- package/server/walk-away.d.ts +0 -22
- package/server/walk-away.js +0 -41
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type EmbeddingInput, type EmbeddingProfile, type EmbeddingResult, type EmbeddingRules, type EmbeddingTask, type Protocol, type TraceRecord } from '@theoremjs/agents';
|
|
2
|
+
import type { TraceFeed } from './trace-feed.ts';
|
|
3
|
+
import { type HttpOptions } from './transport.ts';
|
|
4
|
+
export type EmbeddingInterface = {
|
|
5
|
+
type: 'embedding';
|
|
6
|
+
identity: {
|
|
7
|
+
handle: string;
|
|
8
|
+
};
|
|
9
|
+
/** The profile's inputs held to what its default model takes. */
|
|
10
|
+
inputs: EmbeddingRules;
|
|
11
|
+
observability?: {
|
|
12
|
+
record: boolean;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The interface a client reads: the profile's inputs held to its default model's limits. Pass
|
|
17
|
+
* `protocols` when a model speaks a protocol of your own.
|
|
18
|
+
*/
|
|
19
|
+
export declare function embeddingInterface(profile: Pick<EmbeddingProfile, 'identity' | 'inputs' | 'observability' | 'models' | 'defaultModel'>, protocols?: readonly Protocol[]): EmbeddingInterface;
|
|
20
|
+
export type EmbeddingReply = {
|
|
21
|
+
result: EmbeddingResult;
|
|
22
|
+
traces?: TraceRecord[];
|
|
23
|
+
};
|
|
24
|
+
/** What an embedding asks beyond its inputs: a named model, and what the inputs are for. */
|
|
25
|
+
export type EmbedOptions = {
|
|
26
|
+
model?: string;
|
|
27
|
+
task?: EmbeddingTask;
|
|
28
|
+
};
|
|
29
|
+
export interface EmbeddingTransport {
|
|
30
|
+
describe(signal?: AbortSignal): Promise<EmbeddingInterface>;
|
|
31
|
+
embed(input: EmbeddingInput | readonly EmbeddingInput[], options?: EmbedOptions, signal?: AbortSignal): Promise<EmbeddingReply>;
|
|
32
|
+
traces?: TraceFeed;
|
|
33
|
+
}
|
|
34
|
+
export declare function readEmbeddingReply(raw: unknown): EmbeddingReply;
|
|
35
|
+
/** POSTs an embedding and reads its reply; a failure keeps the trace records its body carried. */
|
|
36
|
+
export declare function postEmbedding(url: string, body: unknown, signal: AbortSignal | undefined, options?: HttpOptions): Promise<EmbeddingReply>;
|
|
37
|
+
/** Transport for a host mounted with `createTheoremEmbeddingHandler`. Default endpoint `/api/embedding`. */
|
|
38
|
+
export declare function createEmbeddingTransport(options?: HttpOptions & {
|
|
39
|
+
endpoint?: string;
|
|
40
|
+
}): EmbeddingTransport;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { embeddingModelLimits, embeddingRules, resolveObservabilityPolicy, traceRecordSchema, z, } from '@theoremjs/agents';
|
|
2
|
+
import { postWithTraces } from "./decision-transport.js";
|
|
3
|
+
import { fetchJson } from "./transport.js";
|
|
4
|
+
import { checkWire } from "./wire-line.js";
|
|
5
|
+
/**
|
|
6
|
+
* The interface a client reads: the profile's inputs held to its default model's limits. Pass
|
|
7
|
+
* `protocols` when a model speaks a protocol of your own.
|
|
8
|
+
*/
|
|
9
|
+
export function embeddingInterface(profile, protocols = []) {
|
|
10
|
+
const binding = profile.models[profile.defaultModel];
|
|
11
|
+
return {
|
|
12
|
+
type: 'embedding',
|
|
13
|
+
identity: { handle: profile.identity.handle },
|
|
14
|
+
inputs: embeddingRules(profile.inputs, binding ? embeddingModelLimits(binding, protocols) : undefined),
|
|
15
|
+
...(profile.observability
|
|
16
|
+
? { observability: { record: resolveObservabilityPolicy(profile.observability).record } }
|
|
17
|
+
: {}),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const embeddingReplySchema = z.object({
|
|
21
|
+
result: z.object({
|
|
22
|
+
model: z.string().min(1),
|
|
23
|
+
embeddings: z.array(z
|
|
24
|
+
.object({
|
|
25
|
+
index: z.number().int().nonnegative(),
|
|
26
|
+
values: z.array(z.number()),
|
|
27
|
+
dimensions: z.number().int().positive(),
|
|
28
|
+
})
|
|
29
|
+
.refine((embedding) => embedding.values.length === embedding.dimensions, 'vector length must match dimensions')),
|
|
30
|
+
usage: z
|
|
31
|
+
.object({
|
|
32
|
+
inputTokens: z.number().int().nonnegative().optional(),
|
|
33
|
+
costUsd: z.number().nonnegative().optional(),
|
|
34
|
+
estimated: z.literal(true).optional(),
|
|
35
|
+
})
|
|
36
|
+
.optional(),
|
|
37
|
+
}),
|
|
38
|
+
traces: z.array(traceRecordSchema).optional(),
|
|
39
|
+
});
|
|
40
|
+
const describedSchema = z.object({
|
|
41
|
+
interface: z.object({
|
|
42
|
+
type: z.literal('embedding'),
|
|
43
|
+
identity: z.object({ handle: z.string() }),
|
|
44
|
+
inputs: z.object({
|
|
45
|
+
text: z.boolean(),
|
|
46
|
+
attachments: z.object({ accept: z.array(z.string()) }).optional(),
|
|
47
|
+
maxFiles: z.number().optional(),
|
|
48
|
+
maxBytes: z.number().optional(),
|
|
49
|
+
maxTurnBytes: z.number().optional(),
|
|
50
|
+
limitsByMime: z.record(z.string(), z.number()).optional(),
|
|
51
|
+
}),
|
|
52
|
+
observability: z.object({ record: z.boolean() }).optional(),
|
|
53
|
+
}),
|
|
54
|
+
});
|
|
55
|
+
export function readEmbeddingReply(raw) {
|
|
56
|
+
return checkWire(embeddingReplySchema, raw, 'the embedding');
|
|
57
|
+
}
|
|
58
|
+
/** POSTs an embedding and reads its reply; a failure keeps the trace records its body carried. */
|
|
59
|
+
export function postEmbedding(url, body, signal, options = {}) {
|
|
60
|
+
return postWithTraces(url, body, signal, options, readEmbeddingReply);
|
|
61
|
+
}
|
|
62
|
+
/** Transport for a host mounted with `createTheoremEmbeddingHandler`. Default endpoint `/api/embedding`. */
|
|
63
|
+
export function createEmbeddingTransport(options = {}) {
|
|
64
|
+
const base = (options.endpoint ?? '/api/embedding').replace(/\/$/, '');
|
|
65
|
+
return {
|
|
66
|
+
async describe(signal) {
|
|
67
|
+
return checkWire(describedSchema, await fetchJson(base, { signal }, options), 'the embedding description').interface;
|
|
68
|
+
},
|
|
69
|
+
embed: (input, embed, signal) => postEmbedding(`${base}/embed`, { input, ...embed }, signal, options),
|
|
70
|
+
};
|
|
71
|
+
}
|
package/client/failure.d.ts
CHANGED
|
@@ -4,29 +4,26 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
|
-
import { type ErrorKind, type
|
|
8
|
-
import type { AttachmentValidationIssue
|
|
7
|
+
import { type ErrorKind, type LabelOverrides } from '@theoremjs/agents';
|
|
8
|
+
import type { AttachmentValidationIssue } from '@theoremjs/agents/interface';
|
|
9
9
|
export type ClientFailure = {
|
|
10
|
-
/** What the user reads: the host's wording, else the profile
|
|
10
|
+
/** What the user reads: the host's wording, else the profile labels' for the failure. */
|
|
11
11
|
error: string;
|
|
12
12
|
errorKind: ErrorKind;
|
|
13
13
|
/** Raw detail for the builder; never shown to the user. */
|
|
14
14
|
errorInternal?: string;
|
|
15
15
|
};
|
|
16
16
|
/**
|
|
17
|
-
* Word a caught value for the user with the profile's `
|
|
18
|
-
* `
|
|
17
|
+
* Word a caught value for the user with the profile's `labels` (the interface's
|
|
18
|
+
* `labels`). A host-reported failure keeps the host's wording.
|
|
19
19
|
*/
|
|
20
|
-
export declare function clientFailure(err: unknown,
|
|
20
|
+
export declare function clientFailure(err: unknown, labels?: LabelOverrides): ClientFailure;
|
|
21
21
|
/** A turn that did not run or did not finish. */
|
|
22
22
|
export type TurnFailure = ClientFailure & {
|
|
23
|
-
ok: false;
|
|
24
23
|
/** The files the profile refused, one entry per reason (the kernel's check). */
|
|
25
24
|
issues?: AttachmentValidationIssue[];
|
|
26
25
|
/** The user stopped it; nothing to show. */
|
|
27
26
|
aborted?: boolean;
|
|
28
|
-
/** The conversation with what the turn got through before it stopped; the next turn resumes from it. */
|
|
29
|
-
session?: InterfaceTurnSession;
|
|
30
27
|
};
|
|
31
28
|
/** A caught value as a turn failure; an aborted `signal` marks it the user's stop. */
|
|
32
|
-
export declare function turnFailure(err: unknown,
|
|
29
|
+
export declare function turnFailure(err: unknown, labels?: LabelOverrides, signal?: AbortSignal): TurnFailure;
|
package/client/failure.js
CHANGED
|
@@ -4,21 +4,21 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
|
-
import { describeError, errorKind,
|
|
7
|
+
import { describeError, errorKind, labelText, publicError, } from '@theoremjs/agents';
|
|
8
8
|
import { isTheoremStreamError } from "./transport.js";
|
|
9
9
|
/**
|
|
10
|
-
* Word a caught value for the user with the profile's `
|
|
11
|
-
* `
|
|
10
|
+
* Word a caught value for the user with the profile's `labels` (the interface's
|
|
11
|
+
* `labels`). A host-reported failure keeps the host's wording.
|
|
12
12
|
*/
|
|
13
|
-
export function clientFailure(err,
|
|
13
|
+
export function clientFailure(err, labels) {
|
|
14
14
|
if (isTheoremStreamError(err)) {
|
|
15
15
|
return {
|
|
16
|
-
error: err.publicMessage ??
|
|
16
|
+
error: err.publicMessage ?? labelText(`error.${err.kind}`, {}, labels),
|
|
17
17
|
errorKind: err.kind,
|
|
18
18
|
...(err.internalMessage ? { errorInternal: err.internalMessage } : {}),
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
-
const error = publicError(err,
|
|
21
|
+
const error = publicError(err, labels);
|
|
22
22
|
const internal = describeError(err);
|
|
23
23
|
return {
|
|
24
24
|
error,
|
|
@@ -27,8 +27,8 @@ export function clientFailure(err, lexicon) {
|
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
/** A caught value as a turn failure; an aborted `signal` marks it the user's stop. */
|
|
30
|
-
export function turnFailure(err,
|
|
31
|
-
const failure = clientFailure(err,
|
|
30
|
+
export function turnFailure(err, labels, signal) {
|
|
31
|
+
const failure = clientFailure(err, labels);
|
|
32
32
|
const aborted = failure.errorKind === 'cancelled' || signal?.aborted === true;
|
|
33
|
-
return {
|
|
33
|
+
return { ...failure, ...(aborted ? { aborted: true } : {}) };
|
|
34
34
|
}
|
package/client/host-call.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export type HostCallState = {
|
|
|
20
20
|
/** The phase a call is in; a stream that broke is an error, one that stopped short a cancel. */
|
|
21
21
|
export declare function hostCallStatus({ call, failure, isRunning }: HostCallState): HostCallStatus;
|
|
22
22
|
/** Why a call failed, as a banner reads it: it never reached its tool, or the tool failed. */
|
|
23
|
-
export declare function hostCallFailure({ call, failure }: HostCallState): {
|
|
23
|
+
export declare function hostCallFailure({ call, failure, }: HostCallState): {
|
|
24
24
|
title: string;
|
|
25
25
|
description?: string;
|
|
26
26
|
} | null;
|
package/client/host-call.js
CHANGED
|
@@ -6,7 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { sampleFromSchema } from "./schema-fields.js";
|
|
8
8
|
import { json } from "./shaped-data.js";
|
|
9
|
-
const SETTLED = new Set([
|
|
9
|
+
const SETTLED = new Set([
|
|
10
|
+
'gate',
|
|
11
|
+
'complete',
|
|
12
|
+
'error',
|
|
13
|
+
'cancel',
|
|
14
|
+
]);
|
|
10
15
|
/** The phase a call is in; a stream that broke is an error, one that stopped short a cancel. */
|
|
11
16
|
export function hostCallStatus({ call, failure, isRunning }) {
|
|
12
17
|
const phase = call?.state?.phase;
|
|
@@ -17,7 +22,7 @@ export function hostCallStatus({ call, failure, isRunning }) {
|
|
|
17
22
|
return isRunning ? 'running' : 'cancel';
|
|
18
23
|
}
|
|
19
24
|
/** Why a call failed, as a banner reads it: it never reached its tool, or the tool failed. */
|
|
20
|
-
export function hostCallFailure({ call, failure }) {
|
|
25
|
+
export function hostCallFailure({ call, failure, }) {
|
|
21
26
|
if (failure)
|
|
22
27
|
return { title: failure.error };
|
|
23
28
|
const state = call?.state;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* @module
|
|
7
7
|
*/
|
|
8
8
|
import { type Profile, type ToolAccess, type ToolPermission, z } from '@theoremjs/agents';
|
|
9
|
+
import type { PageDispatchResult } from './page-dispatch.ts';
|
|
9
10
|
import type { TraceFeed } from './trace-feed.ts';
|
|
10
11
|
import { type HttpOptions, type TheoremInvokeRequest, type TurnEventSink } from './transport.ts';
|
|
11
12
|
/** How a tool reaches its work: a function on the host, an HTTP endpoint, or an MCP server. */
|
|
@@ -54,6 +55,7 @@ export type TheoremHostCallRequest = {
|
|
|
54
55
|
/** A `/call` body as `createTheoremHostHandler` reads it. */
|
|
55
56
|
export declare const theoremHostCallRequestSchema: z.ZodType<TheoremHostCallRequest>;
|
|
56
57
|
export interface HostTransport {
|
|
58
|
+
pageResult?(result: PageDispatchResult, signal?: AbortSignal): Promise<void>;
|
|
57
59
|
describe(signal?: AbortSignal): Promise<HostInterface>;
|
|
58
60
|
/** Runs one tool; its events stream to `onEvent`, ending in `done`. */
|
|
59
61
|
call(request: TheoremHostCallRequest, onEvent: TurnEventSink, signal?: AbortSignal): Promise<void>;
|
package/client/host-transport.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @module
|
|
7
7
|
*/
|
|
8
8
|
import { resolveObservabilityPolicy, TOOL_ACCESS, TOOL_PERMISSION, TURN_EVENT_SCHEMAS, z, } from '@theoremjs/agents';
|
|
9
|
-
import { fetchJson, postNdjson, } from "./transport.js";
|
|
9
|
+
import { fetchJson, postJson, postNdjson, } from "./transport.js";
|
|
10
10
|
import { checkWire } from "./wire-line.js";
|
|
11
11
|
/** How a tool reaches its work: a function on the host, an HTTP endpoint, or an MCP server. */
|
|
12
12
|
export const HOST_TOOL_KINDS = ['function', 'http', 'mcp'];
|
|
@@ -64,8 +64,9 @@ const describedSchema = z.object({
|
|
|
64
64
|
export function createHostTransport(options = {}) {
|
|
65
65
|
const base = (options.endpoint ?? '/api/host').replace(/\/$/, '');
|
|
66
66
|
return {
|
|
67
|
+
pageResult: (body, signal) => postJson(`${base}/page-result`, body, { ...options, signal }),
|
|
67
68
|
async describe(signal) {
|
|
68
|
-
//
|
|
69
|
+
// labels-exempt: internal diagnostic; the user reads error.bad_response
|
|
69
70
|
return checkWire(describedSchema, await fetchJson(base, { signal }, options), 'the host description').interface;
|
|
70
71
|
},
|
|
71
72
|
call: (body, onEvent, signal) => postNdjson(`${base}/call`, body, TURN_EVENT_SCHEMAS, onEvent, { ...options, signal }),
|
package/client/index.d.ts
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
export { float32Rms, float32RmsToLevel, INPUT_LEVEL_GAIN, timeDomainBytesToLevel, } from './audio-level.ts';
|
|
2
|
-
export { createDecisionTransport, type DecisionInterface,
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export { type
|
|
7
|
-
export { composerFieldsFromDraft, type ComposerDraftFields } from './decode-composer-draft.ts';
|
|
2
|
+
export { createDecisionTransport, type DecisionInterface, type DecisionQuestionView, type DecisionReply, type DecisionTransport, decisionAssets, decisionInterface, failedDecisionTraces, failedTraces, postDecision, readDecisionReply, withFailedTraces, } from './decision-transport.ts';
|
|
3
|
+
export { type ComposerDraftFields, composerFieldsFromDraft } from './decode-composer-draft.ts';
|
|
4
|
+
export { addUsage, checkEmbeddingInputs, checkInputs, cosineSimilarity, type EmbeddingFields, type EmbeddingInputCheck, type EmbeddingMatch, type EmbeddingMediaPart, type EmbeddingSplit, embeddingBatches, fieldsInput, inputFields, MAX_BATCH_INPUTS, rankMatches, readEmbeddingInput, readEmbeddingInputs, readsAsText, splitText, } from './embedding-input.ts';
|
|
5
|
+
export { type EmbeddingSpace, layoutSpace, layoutSpaceSoon, placeQuery, type SpaceCluster, type SpaceDocument, type SpaceLink, type SpacePoint, } from './embedding-space.ts';
|
|
6
|
+
export { createEmbeddingTransport, type EmbeddingInterface, type EmbeddingReply, type EmbeddingTransport, type EmbedOptions, embeddingInterface, postEmbedding, readEmbeddingReply, } from './embedding-transport.ts';
|
|
8
7
|
export { encodeComposerDraft } from './encode-composer-draft.ts';
|
|
8
|
+
export { filesToPending, pendingAttachmentsToFiles } from './encode-files.ts';
|
|
9
9
|
export { type ClientFailure, clientFailure, type TurnFailure } from './failure.ts';
|
|
10
|
+
export { createHostTransport, HOST_TOOL_KINDS, type HostInterface, type HostToolKind, type HostToolSource, type HostToolView, type HostTransport, hostInterface, type TheoremHostCallRequest, } from './host-transport.ts';
|
|
11
|
+
export type { LiveCallOptions, PageTool, PageToolAnswer, PageTools, } from './live/live-page-tool.ts';
|
|
10
12
|
export { type LiveConnectPhase, type LiveSessionStatus, type LiveState, liveState, } from './live/live-state.ts';
|
|
11
13
|
export type { LiveToolGatePrompt } from './live/live-tool.ts';
|
|
14
|
+
export type { LiveConnection } from './live-client.ts';
|
|
12
15
|
export { LiveSessionClient } from './live-client.ts';
|
|
16
|
+
export type { LiveSocket } from './live-messages.ts';
|
|
13
17
|
export { isOAuthComplete, notifyOAuthComplete } from './oauth-popup.ts';
|
|
14
|
-
export {
|
|
18
|
+
export { type PageDispatchResult, withPageToolDispatch } from './page-dispatch.ts';
|
|
19
|
+
export { keepsConversation, type RunOutcome, resumeInterfaceTool, type StreamView, streamInterfaceDraftTurn, streamInterfaceTurn, } from './run-session.ts';
|
|
20
|
+
export { type JsonSchema, type SchemaControl, type SchemaField, sampleFromSchema, schemaControl, schemaFields, } from './schema-fields.ts';
|
|
21
|
+
export { citationsFromBlock, type SourceCitation, type SourceCitationBlock, } from './source-citations.ts';
|
|
22
|
+
export type { QueryMatch } from './space-geometry.ts';
|
|
23
|
+
export { layoutResult, type ResultChart, type ResultFigure, type ResultImage, type ResultLayout, } from './tool-result.ts';
|
|
15
24
|
export type { AnsweringGate, ToolDecisionAction, ToolGateResolution, } from './tool-resume.ts';
|
|
16
|
-
export { continueGatedToolInvocation
|
|
17
|
-
export { citationsFromBlock, type SourceCitation, type SourceCitationBlock } from './source-citations.ts';
|
|
25
|
+
export { continueGatedToolInvocation } from './tool-resume.ts';
|
|
18
26
|
export { createTraceFeed, type TraceFeed } from './trace-feed.ts';
|
|
19
|
-
export {
|
|
20
|
-
export {
|
|
27
|
+
export { CHAT_LINES, type ChatLine, type ChatLineSink, type ClientTurnEvent, type ConversationLine, type ConversationView, createHttpTransport, fetchConversation, fetchRewound, type HostErrorBody, type HttpOptions, type HttpTransportOptions, hostError, isTheoremStreamError, type MalformedEvent, postJson, postNdjson, readNdjsonStream, type TheoremConversationRequest, type TheoremInvokeRequest, type TheoremReplay, type TheoremRewindRequest, type TheoremRewound, type TheoremSteerRequest, TheoremStreamError, type TheoremTransport, type TheoremTurnInput, type TheoremTurnRequest, type TurnEventSink, type UnsupportedEvent, type WireLines, } from './transport.ts';
|
|
28
|
+
export { buildInvokeRequest, buildTurnRequest, issuedOperationId, type RunScope, type WalkAway, } from './turn-client.ts';
|
package/client/index.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
export { float32Rms, float32RmsToLevel, INPUT_LEVEL_GAIN, timeDomainBytesToLevel, } from "./audio-level.js";
|
|
2
|
-
export { createDecisionTransport, decisionInterface, readDecisionReply, } from "./decision-transport.js";
|
|
3
|
-
export { filesToPending, pendingAttachmentsToFiles } from "./encode-files.js";
|
|
4
|
-
export { createHostTransport, HOST_TOOL_KINDS, hostInterface, } from "./host-transport.js";
|
|
5
|
-
export { layoutResult } from "./tool-result.js";
|
|
6
|
-
export { sampleFromSchema, schemaControl, schemaFields } from "./schema-fields.js";
|
|
2
|
+
export { createDecisionTransport, decisionAssets, decisionInterface, failedDecisionTraces, failedTraces, postDecision, readDecisionReply, withFailedTraces, } from "./decision-transport.js";
|
|
7
3
|
export { composerFieldsFromDraft } from "./decode-composer-draft.js";
|
|
4
|
+
export { addUsage, checkEmbeddingInputs, checkInputs, cosineSimilarity, embeddingBatches, fieldsInput, inputFields, MAX_BATCH_INPUTS, rankMatches, readEmbeddingInput, readEmbeddingInputs, readsAsText, splitText, } from "./embedding-input.js";
|
|
5
|
+
export { layoutSpace, layoutSpaceSoon, placeQuery, } from "./embedding-space.js";
|
|
6
|
+
export { createEmbeddingTransport, embeddingInterface, postEmbedding, readEmbeddingReply, } from "./embedding-transport.js";
|
|
8
7
|
export { encodeComposerDraft } from "./encode-composer-draft.js";
|
|
8
|
+
export { filesToPending, pendingAttachmentsToFiles } from "./encode-files.js";
|
|
9
9
|
export { clientFailure } from "./failure.js";
|
|
10
|
+
export { createHostTransport, HOST_TOOL_KINDS, hostInterface, } from "./host-transport.js";
|
|
10
11
|
export { liveState, } from "./live/live-state.js";
|
|
11
12
|
export { LiveSessionClient } from "./live-client.js";
|
|
12
13
|
export { isOAuthComplete, notifyOAuthComplete } from "./oauth-popup.js";
|
|
13
|
-
export {
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
14
|
+
export { withPageToolDispatch } from "./page-dispatch.js";
|
|
15
|
+
export { keepsConversation, resumeInterfaceTool, streamInterfaceDraftTurn, streamInterfaceTurn, } from "./run-session.js";
|
|
16
|
+
export { sampleFromSchema, schemaControl, schemaFields, } from "./schema-fields.js";
|
|
17
|
+
export { citationsFromBlock, } from "./source-citations.js";
|
|
18
|
+
export { layoutResult, } from "./tool-result.js";
|
|
19
|
+
export { continueGatedToolInvocation } from "./tool-resume.js";
|
|
16
20
|
export { createTraceFeed } from "./trace-feed.js";
|
|
17
|
-
export {
|
|
18
|
-
export {
|
|
21
|
+
export { CHAT_LINES, createHttpTransport, fetchConversation, fetchRewound, hostError, isTheoremStreamError, postJson, postNdjson, readNdjsonStream, TheoremStreamError, } from "./transport.js";
|
|
22
|
+
export { buildInvokeRequest, buildTurnRequest, issuedOperationId, } from "./turn-client.js";
|
package/client/ink-waveform.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export declare const INK_WAVE_BAR_COUNT = 21;
|
|
|
2
2
|
export declare const INK_WAVE_HERO_BAR_COUNT = 45;
|
|
3
3
|
/** Per-bar phase offsets — stable undulation, not a fixed bell curve. */
|
|
4
4
|
export declare function inkWavePhases(count: number): number[];
|
|
5
|
-
export type InkWaveStatus = 'disconnected' | 'connecting' | 'connected' | 'ready' | 'listening' | 'speaking' | 'working' | 'error';
|
|
5
|
+
export type InkWaveStatus = 'disconnected' | 'connecting' | 'reconnecting' | 'connected' | 'ready' | 'listening' | 'speaking' | 'working' | 'error';
|
|
6
6
|
export type InkWaveDriver = 'idle' | 'input' | 'output' | 'tool' | 'working' | 'connecting';
|
|
7
7
|
export declare function inkWaveDriver(status: InkWaveStatus, toolActive: boolean, inputLevel: number, outputLevel: number, frozen?: boolean): InkWaveDriver;
|
|
8
8
|
export declare function computeInkBarTargets(args: {
|
package/client/ink-waveform.js
CHANGED
|
@@ -10,7 +10,7 @@ export function inkWaveDriver(status, toolActive, inputLevel, outputLevel, froze
|
|
|
10
10
|
return 'idle';
|
|
11
11
|
if (status === 'disconnected' || status === 'error')
|
|
12
12
|
return 'idle';
|
|
13
|
-
if (status === 'connecting')
|
|
13
|
+
if (status === 'connecting' || status === 'reconnecting')
|
|
14
14
|
return 'connecting';
|
|
15
15
|
if (toolActive)
|
|
16
16
|
return 'tool';
|
|
@@ -46,7 +46,6 @@ export function computeInkBarTargets(args) {
|
|
|
46
46
|
master = 0.28 + 0.18 * Math.sin(t * 5.2) + Math.max(output, input) * 0.45;
|
|
47
47
|
break;
|
|
48
48
|
case 'working':
|
|
49
|
-
// Quiet, slow breathing: the server is reasoning, nobody is speaking.
|
|
50
49
|
master = 0.16 + 0.08 * Math.sin(t * 2.2) + Math.max(output, input) * 0.45;
|
|
51
50
|
break;
|
|
52
51
|
case 'connecting':
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TheoremStreamError } from "../transport.js";
|
|
2
2
|
/**
|
|
3
3
|
* A failed tool step as the host reported it: the user reads its wording (else
|
|
4
|
-
* the
|
|
4
|
+
* the labels' for its kind); the model's `message` stays builder detail.
|
|
5
5
|
*/
|
|
6
6
|
function toolStepFailure(failure) {
|
|
7
7
|
return new TheoremStreamError(failure.kind, failure.error, failure.message);
|
|
@@ -1,23 +1,53 @@
|
|
|
1
|
+
import type { TurnEvent } from '@theoremjs/agents';
|
|
2
|
+
import { type TranscriptBlock } from '@theoremjs/agents/interface';
|
|
3
|
+
/** What the agent did between its lines of speech: the events the chat folds into a reply's work. */
|
|
4
|
+
export type LiveWorkEvent = Extract<TurnEvent, {
|
|
5
|
+
type: 'thought' | 'tool' | 'citation';
|
|
6
|
+
}>;
|
|
7
|
+
/** A line of speech, or the work the agent did before its next one. */
|
|
1
8
|
export type LiveCaptionTurn = {
|
|
2
9
|
id: string;
|
|
3
10
|
role: 'user' | 'agent';
|
|
4
11
|
text: string;
|
|
12
|
+
} | {
|
|
13
|
+
id: string;
|
|
14
|
+
role: 'work';
|
|
15
|
+
events: LiveWorkEvent[];
|
|
5
16
|
};
|
|
6
17
|
export type LiveCaptionState = {
|
|
7
18
|
turns: LiveCaptionTurn[];
|
|
8
19
|
interimUser: string;
|
|
9
20
|
interimAgent: string;
|
|
21
|
+
/** The agent has worked and its reply has not ended. */
|
|
22
|
+
working: boolean;
|
|
10
23
|
};
|
|
11
24
|
export type ApplyLiveTranscriptOptions = {
|
|
12
25
|
/** Commit as a new turn even when the last turn shares the same role (explicit text send). */
|
|
13
26
|
forceNew?: boolean;
|
|
14
27
|
};
|
|
15
28
|
export declare function emptyLiveCaptionState(): LiveCaptionState;
|
|
29
|
+
/** Keep this call's lines so the next call starts below a divider. A call with no lines adds nothing. */
|
|
30
|
+
export declare function stashLiveCaptionCall(past: LiveCaptionTurn[][], turns: readonly LiveCaptionTurn[]): LiveCaptionTurn[][];
|
|
16
31
|
/**
|
|
17
32
|
* Fold streaming ASR deltas into turn-by-turn caption lines.
|
|
18
33
|
* Final chunks for the same role append to the open turn; role switches start a new turn.
|
|
19
34
|
*/
|
|
20
35
|
export declare function applyLiveTranscript(state: LiveCaptionState, text: string, isUser: boolean, interim?: boolean, options?: ApplyLiveTranscriptOptions): LiveCaptionState;
|
|
36
|
+
/**
|
|
37
|
+
* Fold a live turn event into the captions. A thought, a tool call, or a citation joins the open
|
|
38
|
+
* work, else starts it; a tool call's later events go to the work that made the call. `done` ends
|
|
39
|
+
* the reply.
|
|
40
|
+
*/
|
|
41
|
+
export declare function applyLiveTurnEvent(state: LiveCaptionState, event: TurnEvent): LiveCaptionState;
|
|
42
|
+
export type LiveCaptionTranscript = {
|
|
43
|
+
blocks: TranscriptBlock[];
|
|
44
|
+
/** The first block of each call after the first: where one call's captions end and the next begin. */
|
|
45
|
+
callStarts: string[];
|
|
46
|
+
};
|
|
47
|
+
/** Captions as the chat's transcript blocks: past calls, this call, then the lines still being heard. */
|
|
48
|
+
export declare function liveCaptionTranscript(pastCalls: readonly (readonly LiveCaptionTurn[])[], { turns, interimUser, interimAgent }: LiveCaptionState): LiveCaptionTranscript;
|
|
49
|
+
/** The agent is mid-reply: a line of its speech is still arriving, or it is working. */
|
|
50
|
+
export declare function liveCaptionStreaming({ working, interimAgent }: LiveCaptionState): boolean;
|
|
21
51
|
/** Clear streaming partials when a live turn completes or is interrupted. */
|
|
22
52
|
export declare function clearLiveCaptionInterim(state: LiveCaptionState): LiveCaptionState;
|
|
23
53
|
export declare function latestLiveCaptionTurnId(state: LiveCaptionState): string | null;
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
import { foldTurnEvents } from '@theoremjs/agents/interface';
|
|
1
2
|
export function emptyLiveCaptionState() {
|
|
2
|
-
return { turns: [], interimUser: '', interimAgent: '' };
|
|
3
|
+
return { turns: [], interimUser: '', interimAgent: '', working: false };
|
|
4
|
+
}
|
|
5
|
+
/** Keep this call's lines so the next call starts below a divider. A call with no lines adds nothing. */
|
|
6
|
+
export function stashLiveCaptionCall(past, turns) {
|
|
7
|
+
if (turns.length === 0)
|
|
8
|
+
return past;
|
|
9
|
+
return [...past, [...turns]];
|
|
3
10
|
}
|
|
4
11
|
function mergeInterimWithFinal(interim, final) {
|
|
5
12
|
if (!interim)
|
|
@@ -54,11 +61,108 @@ export function applyLiveTranscript(state, text, isUser, interim, options) {
|
|
|
54
61
|
});
|
|
55
62
|
}
|
|
56
63
|
return {
|
|
64
|
+
...state,
|
|
57
65
|
turns,
|
|
58
66
|
interimUser: isUser ? '' : state.interimUser,
|
|
59
67
|
interimAgent: isUser ? state.interimAgent : '',
|
|
60
68
|
};
|
|
61
69
|
}
|
|
70
|
+
function isWorkEvent(event) {
|
|
71
|
+
return event.type === 'thought' || event.type === 'tool' || event.type === 'citation';
|
|
72
|
+
}
|
|
73
|
+
/** The work that made the call `callId`: where the call's later events go. -1 when no work made it. */
|
|
74
|
+
function callWorkIndex(turns, callId) {
|
|
75
|
+
return turns.findIndex((turn) => turn.role === 'work' &&
|
|
76
|
+
turn.events.some((made) => made.type === 'tool' && made.tool.callId === callId));
|
|
77
|
+
}
|
|
78
|
+
/** Which line takes `event`: `turns.length` starts a new one, -1 leaves the event out. */
|
|
79
|
+
function workIndex(turns, event) {
|
|
80
|
+
if (event.type === 'tool' && event.tool.phase !== undefined) {
|
|
81
|
+
// why: The call view's dialog asks a gate's question; the transcript's card would ask it twice.
|
|
82
|
+
return event.tool.phase === 'gate' ? -1 : callWorkIndex(turns, event.tool.callId);
|
|
83
|
+
}
|
|
84
|
+
const open = turns.at(-1)?.role === 'work';
|
|
85
|
+
// why: The thought guard releases a held blank tail after the speech; it is not work of its own.
|
|
86
|
+
if (event.type === 'thought' && !(open ? event.text : event.text.trim()))
|
|
87
|
+
return -1;
|
|
88
|
+
return open ? turns.length - 1 : turns.length;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Fold a live turn event into the captions. A thought, a tool call, or a citation joins the open
|
|
92
|
+
* work, else starts it; a tool call's later events go to the work that made the call. `done` ends
|
|
93
|
+
* the reply.
|
|
94
|
+
*/
|
|
95
|
+
export function applyLiveTurnEvent(state, event) {
|
|
96
|
+
if (event.type === 'done')
|
|
97
|
+
return state.working ? { ...state, working: false } : state;
|
|
98
|
+
if (!isWorkEvent(event))
|
|
99
|
+
return state;
|
|
100
|
+
const at = workIndex(state.turns, event);
|
|
101
|
+
if (at === -1)
|
|
102
|
+
return state;
|
|
103
|
+
const turns = [...state.turns];
|
|
104
|
+
const work = turns[at];
|
|
105
|
+
turns[at] =
|
|
106
|
+
work?.role === 'work'
|
|
107
|
+
? { ...work, events: [...work.events, event] }
|
|
108
|
+
: { id: nextTurnId(state), role: 'work', events: [event] };
|
|
109
|
+
return { ...state, turns, working: true };
|
|
110
|
+
}
|
|
111
|
+
/** A line as the chat's blocks: speech is one, work is what the chat folds its events into. */
|
|
112
|
+
function turnBlocks(turn) {
|
|
113
|
+
if (turn.role === 'work')
|
|
114
|
+
return foldTurnEvents(turn.events, { idPrefix: turn.id });
|
|
115
|
+
return [{ id: turn.id, kind: turn.role === 'user' ? 'user-text' : 'text', text: turn.text }];
|
|
116
|
+
}
|
|
117
|
+
/** How much of a broken-off line the next one must repeat to count as starting it again. */
|
|
118
|
+
const RESTART_MATCH = 24;
|
|
119
|
+
function sharedStart(a, b) {
|
|
120
|
+
let length = 0;
|
|
121
|
+
while (length < a.length && length < b.length && a[length] === b[length])
|
|
122
|
+
length += 1;
|
|
123
|
+
return length;
|
|
124
|
+
}
|
|
125
|
+
/** The later line opens with the same words as the earlier one, or with all of it when it is short. */
|
|
126
|
+
function restarts(earlier, later) {
|
|
127
|
+
const start = earlier.trim();
|
|
128
|
+
return sharedStart(start, later.trim()) >= Math.min(start.length, RESTART_MATCH);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Drops speech the agent broke off and began again in the same reply, which a
|
|
132
|
+
* tool call mid-sentence makes it do: the line that starts over replaces it.
|
|
133
|
+
*/
|
|
134
|
+
function withoutRestarts(lines) {
|
|
135
|
+
return lines.filter((line, index) => {
|
|
136
|
+
if (line.role !== 'agent')
|
|
137
|
+
return true;
|
|
138
|
+
const after = lines.slice(index + 1);
|
|
139
|
+
const nextUser = after.findIndex((later) => later.role === 'user');
|
|
140
|
+
const reply = nextUser === -1 ? after : after.slice(0, nextUser);
|
|
141
|
+
return !reply.some((later) => later.role === 'agent' && restarts(line.text, later.text));
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/** Captions as the chat's transcript blocks: past calls, this call, then the lines still being heard. */
|
|
145
|
+
export function liveCaptionTranscript(pastCalls, { turns, interimUser, interimAgent }) {
|
|
146
|
+
const current = [...turns];
|
|
147
|
+
if (interimUser)
|
|
148
|
+
current.push({ id: 'interim-user', role: 'user', text: interimUser });
|
|
149
|
+
if (interimAgent)
|
|
150
|
+
current.push({ id: 'interim-agent', role: 'agent', text: interimAgent });
|
|
151
|
+
const calls = [
|
|
152
|
+
...pastCalls.map((call, index) => call.map((turn) => ({ ...turn, id: `${String(index)}:${turn.id}` }))),
|
|
153
|
+
current,
|
|
154
|
+
]
|
|
155
|
+
.map((call) => withoutRestarts(call).flatMap(turnBlocks))
|
|
156
|
+
.filter((call) => call.length > 0);
|
|
157
|
+
return {
|
|
158
|
+
blocks: calls.flat(),
|
|
159
|
+
callStarts: calls.slice(1).map((call) => call[0].id),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/** The agent is mid-reply: a line of its speech is still arriving, or it is working. */
|
|
163
|
+
export function liveCaptionStreaming({ working, interimAgent }) {
|
|
164
|
+
return interimAgent !== '' || working;
|
|
165
|
+
}
|
|
62
166
|
/** Clear streaming partials when a live turn completes or is interrupted. */
|
|
63
167
|
export function clearLiveCaptionInterim(state) {
|
|
64
168
|
if (!state.interimUser && !state.interimAgent)
|
|
@@ -13,7 +13,11 @@ export function liveTranscriptFromEvidence(event) {
|
|
|
13
13
|
return null;
|
|
14
14
|
if (evidence.kind !== 'input_transcription' && evidence.kind !== 'output_transcription')
|
|
15
15
|
return null;
|
|
16
|
-
return {
|
|
16
|
+
return {
|
|
17
|
+
text,
|
|
18
|
+
isUser: evidence.kind === 'input_transcription',
|
|
19
|
+
interim: evidence.interim === true,
|
|
20
|
+
};
|
|
17
21
|
}
|
|
18
22
|
/**
|
|
19
23
|
* Fold a tool turn event into cancelled-id / runnable-call lists. The model's
|
|
@@ -22,7 +26,12 @@ export function liveTranscriptFromEvidence(event) {
|
|
|
22
26
|
*/
|
|
23
27
|
export function applyLiveToolTurnEvent(tool, accum) {
|
|
24
28
|
if (tool.phase === undefined) {
|
|
25
|
-
accum.toolCalls.push({
|
|
29
|
+
accum.toolCalls.push({
|
|
30
|
+
id: tool.callId,
|
|
31
|
+
name: tool.name,
|
|
32
|
+
arguments: tool.arguments,
|
|
33
|
+
...(tool.operationId ? { operationId: tool.operationId } : {}),
|
|
34
|
+
});
|
|
26
35
|
return;
|
|
27
36
|
}
|
|
28
37
|
if (tool.phase === 'cancel') {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** The page's result for a call: the tool's output schema checks it, then the model reads it. */
|
|
2
|
+
export type PageToolAnswer = {
|
|
3
|
+
output: unknown;
|
|
4
|
+
};
|
|
5
|
+
/** One page tool: what the page does for a call the model made, and what it tells the model. */
|
|
6
|
+
export type PageTool = (args: Record<string, unknown>, call: {
|
|
7
|
+
callId: string;
|
|
8
|
+
operationId: string;
|
|
9
|
+
}) => PageToolAnswer | Promise<PageToolAnswer>;
|
|
10
|
+
/**
|
|
11
|
+
* The page's tools, by name: each answers a tool the profile declares with
|
|
12
|
+
* `answeredBy: 'page'`. Any other tool runs on the relay, gates and all.
|
|
13
|
+
*/
|
|
14
|
+
export type PageTools = Record<string, PageTool>;
|
|
15
|
+
/** What a host gives a live call. */
|
|
16
|
+
export type LiveCallOptions = {
|
|
17
|
+
/** The value chosen for each of the profile's `inputs.slots`, read as the call starts. */
|
|
18
|
+
slots?: Record<string, string>;
|
|
19
|
+
/**
|
|
20
|
+
* What the page tells the agent: any JSON. It goes with the call's opening, and
|
|
21
|
+
* again whenever it changes, as background the agent reads without replying.
|
|
22
|
+
*/
|
|
23
|
+
context?: unknown;
|
|
24
|
+
pageTools?: PageTools;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Where the page's tools and the profile's disagree, for the builder: a page
|
|
28
|
+
* tool with no handler here leaves the model unanswered, and a handler for a
|
|
29
|
+
* tool the profile does not give the page never runs.
|
|
30
|
+
*/
|
|
31
|
+
export declare function pageToolMismatch(declared: readonly string[], handled: readonly string[]): {
|
|
32
|
+
unanswered: string[];
|
|
33
|
+
unused: string[];
|
|
34
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the page's tools and the profile's disagree, for the builder: a page
|
|
3
|
+
* tool with no handler here leaves the model unanswered, and a handler for a
|
|
4
|
+
* tool the profile does not give the page never runs.
|
|
5
|
+
*/
|
|
6
|
+
export function pageToolMismatch(declared, handled) {
|
|
7
|
+
return {
|
|
8
|
+
unanswered: declared.filter((name) => !handled.includes(name)),
|
|
9
|
+
unused: handled.filter((name) => !declared.includes(name)),
|
|
10
|
+
};
|
|
11
|
+
}
|