@datalayer/agent-runtimes 1.5.59 → 1.5.60
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/dist-embed/chunks/{ChatView-lgDCrM9e.js → ChatView-CjD1TsaE.js} +1 -1
- package/dist-embed/datalayer-app-main.js +46 -46
- package/lib/apps/plugins/chat/ChatView.js +3 -1
- package/lib/chat/ChatFloating.js +4 -0
- package/lib/chat/base/ChatBase.js +26 -4
- package/lib/chat/prompt/InputPrompt.js +3 -0
- package/lib/chat/prompt/stack.d.ts +22 -0
- package/lib/chat/prompt/stack.js +40 -1
- package/lib/models/PageTag.d.ts +1 -1
- package/lib/protocols/AGUIAdapter.d.ts +8 -0
- package/lib/protocols/AGUIAdapter.js +26 -0
- package/lib/types/chat.d.ts +6 -0
- package/lib/voice/sayAloud.d.ts +7 -0
- package/lib/voice/sayAloud.js +7 -0
- package/package.json +1 -1
|
@@ -1720,7 +1720,9 @@ export default function ChatView({ workspace }) {
|
|
|
1720
1720
|
playbackLevel: () => controlsRef.current?.voiceLevel?.() ?? 0,
|
|
1721
1721
|
// Hands-free (VO-12, VO-13): the run in progress, stopped when
|
|
1722
1722
|
// the person speaks over it, and the answer marked interrupted.
|
|
1723
|
-
running: busy,
|
|
1723
|
+
running: busy,
|
|
1724
|
+
// Spoken over, the application's session stays open (VO-46).
|
|
1725
|
+
onStopRun: () => (controlsRef.current?.interrupt ?? controlsRef.current?.stop)?.(), onInterrupted: () => controlsRef.current?.markInterrupted(), onInterimChange: setVoiceInterim,
|
|
1724
1726
|
// The menu offers when answers are heard where the
|
|
1725
1727
|
// application lets them be (VO-21, VO-30).
|
|
1726
1728
|
answers: Boolean(config?.voice?.output &&
|
package/lib/chat/ChatFloating.js
CHANGED
|
@@ -10,6 +10,7 @@ import { ButtonGlow } from './display/ButtonGlow';
|
|
|
10
10
|
import { useViewportDrag } from './useViewportDrag';
|
|
11
11
|
import { disabledChatViewModes, resolveMountPoint } from './viewModes';
|
|
12
12
|
import { AssistantStage } from './assistant/AssistantStage';
|
|
13
|
+
import { ASSISTANT_CLEARANCE_VAR } from './assistant/clearance';
|
|
13
14
|
import { decisionsAskerAt } from './assistant/decisions';
|
|
14
15
|
import { DecisionAsk } from './assistant/DecisionAsk';
|
|
15
16
|
import { useSpokenAnswers } from '../voice/useSpokenAnswers';
|
|
@@ -854,6 +855,9 @@ export function ChatFloating({ endpoint, protocol: protocolProp, useStore: useSt
|
|
|
854
855
|
: undefined;
|
|
855
856
|
/* The window itself; where it goes is decided below. */
|
|
856
857
|
const chatWindow = (_jsxs(Box, { ref: popupRef, className: className, sx: {
|
|
858
|
+
// Its own conversation stands beside the character, never under it:
|
|
859
|
+
// its prompt's controls keep their own padding (T-27).
|
|
860
|
+
...(assistantShown ? { [ASSISTANT_CLEARANCE_VAR]: 'initial' } : {}),
|
|
857
861
|
position: viewMode === 'panel' && !isMobile && (panelInFlow || dockedInMount)
|
|
858
862
|
? 'relative'
|
|
859
863
|
: 'fixed',
|
|
@@ -59,7 +59,7 @@ import { useVoiceSettings } from '../../voice/useVoiceSettings';
|
|
|
59
59
|
import { useVoiceSpeaker } from '../../voice/useVoiceSpeaker';
|
|
60
60
|
import { useSpokenAnswers } from '../../voice/useSpokenAnswers';
|
|
61
61
|
import { saidMessageId, useSaidSentence } from '../../voice/captions';
|
|
62
|
-
import { lineToSay } from '../../voice/sayAloud';
|
|
62
|
+
import { isVoiceInterrupted, lineToSay, } from '../../voice/sayAloud';
|
|
63
63
|
import { isFloatingChatViewMode } from '../viewModes';
|
|
64
64
|
import { ToolApprovalBanner, ToolApprovalDialog, } from '../tools';
|
|
65
65
|
import { initialModelId, isOffered, offeredModels as offeredModelsFor, } from './modelChoice';
|
|
@@ -2986,6 +2986,16 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
2986
2986
|
*/
|
|
2987
2987
|
const [voiceSaid, setVoiceSaid] = useState('');
|
|
2988
2988
|
voiceSayRef.current = activity => {
|
|
2989
|
+
if (isVoiceInterrupted(activity)) {
|
|
2990
|
+
// Spoken over, the runtime says (VO-46): its voice stops.
|
|
2991
|
+
if (onStopSpeaking) {
|
|
2992
|
+
onStopSpeaking();
|
|
2993
|
+
}
|
|
2994
|
+
else if (ownVoice.speaker) {
|
|
2995
|
+
ownVoice.stop();
|
|
2996
|
+
}
|
|
2997
|
+
return;
|
|
2998
|
+
}
|
|
2989
2999
|
const line = lineToSay(activity);
|
|
2990
3000
|
if (!line) {
|
|
2991
3001
|
return;
|
|
@@ -3251,7 +3261,9 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
3251
3261
|
applyServerApprovalDecision,
|
|
3252
3262
|
]);
|
|
3253
3263
|
// ---- handleStop ----
|
|
3254
|
-
|
|
3264
|
+
// Spoken over (VO-46, VO-13), an application's session is told so and
|
|
3265
|
+
// stays open; Stop leaves it to be resumed.
|
|
3266
|
+
const stopTurn = useCallback((spokenOver) => {
|
|
3255
3267
|
stoppedRef.current = true;
|
|
3256
3268
|
abortControllerRef.current?.abort();
|
|
3257
3269
|
// Best-effort cancellation without tearing down adapter/session.
|
|
@@ -3262,7 +3274,10 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
3262
3274
|
adapter.stopGeneration();
|
|
3263
3275
|
}
|
|
3264
3276
|
// Also tell the backend to stop (server-side cancellation).
|
|
3265
|
-
if (typeof adapter.
|
|
3277
|
+
if (spokenOver && typeof adapter.interruptSession === 'function') {
|
|
3278
|
+
void adapter.interruptSession().catch(() => { });
|
|
3279
|
+
}
|
|
3280
|
+
else if (typeof adapter.terminateSession === 'function') {
|
|
3266
3281
|
void adapter.terminateSession().catch(() => { });
|
|
3267
3282
|
}
|
|
3268
3283
|
else if (typeof adapter.terminateAgent === 'function') {
|
|
@@ -3314,6 +3329,8 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
3314
3329
|
protocol?.authToken,
|
|
3315
3330
|
protocol?.agentId,
|
|
3316
3331
|
]);
|
|
3332
|
+
const handleStop = useCallback(() => stopTurn(false), [stopTurn]);
|
|
3333
|
+
const handleSpokenOver = useCallback(() => stopTurn(true), [stopTurn]);
|
|
3317
3334
|
/*
|
|
3318
3335
|
* How a message's display presses what it offered (RU-4,
|
|
3319
3336
|
* RU-W): with the token of the offer, as the run's press — which
|
|
@@ -3390,6 +3407,8 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
3390
3407
|
voiceLevel: playbackLevel,
|
|
3391
3408
|
// The answer spoken over, marked for a host's own composer (VO-13).
|
|
3392
3409
|
markInterrupted,
|
|
3410
|
+
// Spoken over: the run ends, the application's session stays open (VO-46).
|
|
3411
|
+
interrupt: handleSpokenOver,
|
|
3393
3412
|
});
|
|
3394
3413
|
return () => {
|
|
3395
3414
|
onSendReady(null);
|
|
@@ -3400,6 +3419,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
3400
3419
|
onSendMessage,
|
|
3401
3420
|
handleSend,
|
|
3402
3421
|
handleStop,
|
|
3422
|
+
handleSpokenOver,
|
|
3403
3423
|
handleNewChat,
|
|
3404
3424
|
stopSpeaking,
|
|
3405
3425
|
playbackLevel,
|
|
@@ -3808,7 +3828,9 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
3808
3828
|
playbackLevel: playbackLevel,
|
|
3809
3829
|
// Hands-free (VO-12, VO-13): the run in progress, stopped when the
|
|
3810
3830
|
// person speaks over it, and the answer marked interrupted.
|
|
3811
|
-
running: isLoading,
|
|
3831
|
+
running: isLoading,
|
|
3832
|
+
// Spoken over, the application's session stays open (VO-46).
|
|
3833
|
+
onStopRun: handleSpokenOver, onInterrupted: markInterrupted, onInterimChange: setVoiceInterim,
|
|
3812
3834
|
// The menu offers when answers are heard where the application
|
|
3813
3835
|
// lets them be (VO-21, VO-30).
|
|
3814
3836
|
answers: Boolean(voice?.output && voice.output !== 'off' && voice.speechUrl), onSpoken: metadata => {
|
|
@@ -151,6 +151,9 @@ export function InputPrompt({ input, setInput, isLoading, kernelStatus, connecti
|
|
|
151
151
|
{
|
|
152
152
|
id: 'session-controls',
|
|
153
153
|
px: padding,
|
|
154
|
+
// Its right end — the voice controls — is never under
|
|
155
|
+
// the floating assistant in its corner (T-27).
|
|
156
|
+
clearsAssistant: true,
|
|
154
157
|
py: 0.5,
|
|
155
158
|
minHeight: 36,
|
|
156
159
|
bordered: true,
|
|
@@ -18,6 +18,19 @@
|
|
|
18
18
|
* @module chat/prompt/stack
|
|
19
19
|
*/
|
|
20
20
|
import type { ReactNode } from 'react';
|
|
21
|
+
/**
|
|
22
|
+
* A theme space step (`px={2}`) as a CSS length, from the theme's scale or
|
|
23
|
+
* else Primer's own.
|
|
24
|
+
*/
|
|
25
|
+
export declare function spaceLength(step: number, scale?: readonly (string | number)[]): string;
|
|
26
|
+
/**
|
|
27
|
+
* The right padding of a band that `clearsAssistant`: the floating
|
|
28
|
+
* assistant's clearance while it stands in a bottom corner (what it covers
|
|
29
|
+
* from the window's right edge, `--assistant-clearance`), so that the
|
|
30
|
+
* character is never drawn over the band's controls and never takes their
|
|
31
|
+
* clicks; else the band's own `padding`, a CSS length.
|
|
32
|
+
*/
|
|
33
|
+
export declare function clearOfAssistant(padding: string): string;
|
|
21
34
|
/** One band in a region. */
|
|
22
35
|
export type PromptStack = {
|
|
23
36
|
/** Identity, for React and for a host reasoning about which band is which. */
|
|
@@ -69,6 +82,15 @@ export type PromptStack = {
|
|
|
69
82
|
* showing the page behind it — a pale stripe down the right-hand side.
|
|
70
83
|
*/
|
|
71
84
|
layout?: 'row' | 'block';
|
|
85
|
+
/**
|
|
86
|
+
* Keep the band's right end clear of the floating assistant while it
|
|
87
|
+
* stands in a bottom corner: its right padding becomes the assistant's
|
|
88
|
+
* clearance (`--assistant-clearance`), so that the character is never
|
|
89
|
+
* drawn over the band's controls and never takes their clicks. For a band
|
|
90
|
+
* that runs to the page's right edge, as the composer's session controls
|
|
91
|
+
* do on an application's page.
|
|
92
|
+
*/
|
|
93
|
+
clearsAssistant?: boolean;
|
|
72
94
|
};
|
|
73
95
|
export type PromptStacksProps = {
|
|
74
96
|
/** The bands, top to bottom. */
|
package/lib/chat/prompt/stack.js
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useTheme } from '@primer/react';
|
|
2
3
|
import { Box } from '@datalayer/primer-addons';
|
|
4
|
+
import { ASSISTANT_CLEARANCE_VAR } from '../assistant/clearance';
|
|
5
|
+
/** Primer's space scale, for a band drawn without a theme. */
|
|
6
|
+
const PRIMER_SPACE = [
|
|
7
|
+
'0',
|
|
8
|
+
'4px',
|
|
9
|
+
'8px',
|
|
10
|
+
'16px',
|
|
11
|
+
'24px',
|
|
12
|
+
'32px',
|
|
13
|
+
'40px',
|
|
14
|
+
'48px',
|
|
15
|
+
'64px',
|
|
16
|
+
];
|
|
17
|
+
/**
|
|
18
|
+
* A theme space step (`px={2}`) as a CSS length, from the theme's scale or
|
|
19
|
+
* else Primer's own.
|
|
20
|
+
*/
|
|
21
|
+
export function spaceLength(step, scale = PRIMER_SPACE) {
|
|
22
|
+
const value = scale[step] ?? PRIMER_SPACE[step] ?? `${step * 8}px`;
|
|
23
|
+
return typeof value === 'number' ? `${value}px` : value;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The right padding of a band that `clearsAssistant`: the floating
|
|
27
|
+
* assistant's clearance while it stands in a bottom corner (what it covers
|
|
28
|
+
* from the window's right edge, `--assistant-clearance`), so that the
|
|
29
|
+
* character is never drawn over the band's controls and never takes their
|
|
30
|
+
* clicks; else the band's own `padding`, a CSS length.
|
|
31
|
+
*/
|
|
32
|
+
export function clearOfAssistant(padding) {
|
|
33
|
+
return `var(${ASSISTANT_CLEARANCE_VAR}, ${padding})`;
|
|
34
|
+
}
|
|
3
35
|
/**
|
|
4
36
|
* Whether a band would draw anything.
|
|
5
37
|
*
|
|
@@ -12,12 +44,19 @@ function isRendered(stack) {
|
|
|
12
44
|
/** Render a region's bands, skipping the ones with nothing to show. */
|
|
13
45
|
export function PromptStacks({ stacks, disabled = false, }) {
|
|
14
46
|
const visible = stacks.filter(isRendered);
|
|
47
|
+
const { theme } = useTheme();
|
|
15
48
|
if (visible.length === 0) {
|
|
16
49
|
return null;
|
|
17
50
|
}
|
|
18
51
|
return (_jsx(_Fragment, { children: visible.map(stack => {
|
|
19
52
|
const isDisabled = disabled || stack.disabled;
|
|
20
|
-
|
|
53
|
+
const px = stack.px ?? 2;
|
|
54
|
+
const right = Array.isArray(px) ? px[px.length - 1] : px;
|
|
55
|
+
return (_jsx(Box, { "data-prompt-stack": stack.id, "aria-disabled": isDisabled || undefined, px: px, "data-clears-assistant": stack.clearsAssistant ? '' : undefined, sx: stack.clearsAssistant
|
|
56
|
+
? {
|
|
57
|
+
paddingRight: clearOfAssistant(spaceLength(right, theme?.space)),
|
|
58
|
+
}
|
|
59
|
+
: undefined, py: stack.py ?? 1, minHeight: stack.minHeight, display: stack.layout === 'block' ? undefined : 'flex', alignItems: stack.layout === 'block' ? undefined : 'center', gap: stack.layout === 'block' ? undefined : (stack.gap ?? 2), pt: stack.pt === undefined ? undefined : stack.pt, pb: stack.pb === undefined ? undefined : stack.pb, borderTop: stack.bordered ? '1px solid' : undefined, borderColor: stack.bordered ? 'border.default' : undefined, bg: stack.subtle ? 'canvas.subtle' : undefined, opacity: isDisabled ? 0.5 : undefined, pointerEvents: isDisabled ? 'none' : undefined, children: stack.content }, stack.id));
|
|
21
60
|
}) }));
|
|
22
61
|
}
|
|
23
62
|
export default PromptStacks;
|
package/lib/models/PageTag.d.ts
CHANGED
|
@@ -15,5 +15,5 @@ export type PageTag = {
|
|
|
15
15
|
export declare const PAGE_TAGE_NONE: PageTag;
|
|
16
16
|
export declare const PAGE_TAGS: PageTag[];
|
|
17
17
|
export declare const PAGE_TAGS_BY_NAME_MAP: Map<PageTagName | undefined, PageTag>;
|
|
18
|
-
export declare const PAGE_TAGS_BY_COLOR_MAP: Map<"
|
|
18
|
+
export declare const PAGE_TAGS_BY_COLOR_MAP: Map<"blue" | "coral" | "gray" | "green" | "indigo" | "lime" | "orange" | "pink" | "purple" | "red" | "teal" | "yellow" | "default" | "lemon", PageTag>;
|
|
19
19
|
export {};
|
|
@@ -66,6 +66,14 @@ export declare class AGUIAdapter extends BaseProtocolAdapter {
|
|
|
66
66
|
* Terminate a running agent thread on the backend
|
|
67
67
|
*/
|
|
68
68
|
terminateAgent(threadId?: string): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* The person spoke over the agent (VO-46, VO-13): an application's
|
|
71
|
+
* session is told (`POST {sessions}/{uid}/interrupt`) — what runs is
|
|
72
|
+
* cancelled, its code's `@app.interrupted` runs, and it stays open for
|
|
73
|
+
* what is said next, where Stop would leave it waiting to be resumed.
|
|
74
|
+
* Another agent's run is stopped, as Stop stops it.
|
|
75
|
+
*/
|
|
76
|
+
interruptSession(threadId?: string): Promise<void>;
|
|
69
77
|
/**
|
|
70
78
|
* Stop an application's session (`POST {sessions}/{uid}/stop`): what runs
|
|
71
79
|
* is cancelled and it waits to be resumed. Its thread is the session; with
|
|
@@ -126,6 +126,32 @@ export class AGUIAdapter extends BaseProtocolAdapter {
|
|
|
126
126
|
// Ignore errors - agent may have already completed
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* The person spoke over the agent (VO-46, VO-13): an application's
|
|
131
|
+
* session is told (`POST {sessions}/{uid}/interrupt`) — what runs is
|
|
132
|
+
* cancelled, its code's `@app.interrupted` runs, and it stays open for
|
|
133
|
+
* what is said next, where Stop would leave it waiting to be resumed.
|
|
134
|
+
* Another agent's run is stopped, as Stop stops it.
|
|
135
|
+
*/
|
|
136
|
+
async interruptSession(threadId) {
|
|
137
|
+
const sessions = this.aguiConfig.appSessions;
|
|
138
|
+
const thread = threadId || this.currentThreadId;
|
|
139
|
+
if (!sessions) {
|
|
140
|
+
await this.terminateAgent(threadId);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (!thread) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const url = `${sessions.replace(/\/+$/, '')}/${encodeURIComponent(thread)}/interrupt`;
|
|
147
|
+
const response = await this.fetchOf()(url, {
|
|
148
|
+
method: 'POST',
|
|
149
|
+
headers: this.buildHeaders(),
|
|
150
|
+
});
|
|
151
|
+
if (!response.ok) {
|
|
152
|
+
throw new Error(`The session ${thread} could not be told it was spoken over (${response.status}).`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
129
155
|
/**
|
|
130
156
|
* Stop an application's session (`POST {sessions}/{uid}/stop`): what runs
|
|
131
157
|
* is cancelled and it waits to be resumed. Its thread is the session; with
|
package/lib/types/chat.d.ts
CHANGED
|
@@ -700,6 +700,12 @@ export interface ChatBaseProps {
|
|
|
700
700
|
* for a host whose composer holds the microphone.
|
|
701
701
|
*/
|
|
702
702
|
markInterrupted: () => void;
|
|
703
|
+
/**
|
|
704
|
+
* The person spoke over the run (VO-46, VO-13): it ends as Stop ends
|
|
705
|
+
* it, but an application's session is told it was spoken over and
|
|
706
|
+
* stays open for what is said next — Stop leaves it to be resumed.
|
|
707
|
+
*/
|
|
708
|
+
interrupt?: () => void;
|
|
703
709
|
} | null) => void;
|
|
704
710
|
/**
|
|
705
711
|
* Reports whether the agent's voice is speaking, for a host that draws the
|
package/lib/voice/sayAloud.d.ts
CHANGED
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
export declare const VOICE_SAY_EVENT = "voice.say";
|
|
15
15
|
/** The running application's voice, at a run's start. */
|
|
16
16
|
export declare const VOICE_SETTINGS_EVENT = "voice.settings";
|
|
17
|
+
/**
|
|
18
|
+
* The person spoke over the agent, the runtime says (VO-46): a client
|
|
19
|
+
* playing its voice stops it.
|
|
20
|
+
*/
|
|
21
|
+
export declare const VOICE_INTERRUPTED_EVENT = "voice.interrupted";
|
|
17
22
|
/** The longest line said aloud: the runtime refuses longer ones. */
|
|
18
23
|
export declare const SAY_ALOUD_MAX_CHARACTERS = 200;
|
|
19
24
|
/** A custom event or data part, as the chat's adapters give it. */
|
|
@@ -21,5 +26,7 @@ export interface VoiceActivity {
|
|
|
21
26
|
type: string;
|
|
22
27
|
data: unknown;
|
|
23
28
|
}
|
|
29
|
+
/** Whether an activity is the runtime's `voice.interrupted` (VO-46). */
|
|
30
|
+
export declare const isVoiceInterrupted: (activity: VoiceActivity) => boolean;
|
|
24
31
|
/** The line a `voice.say` says, or `undefined` for anything else. */
|
|
25
32
|
export declare function lineToSay(activity: VoiceActivity): string | undefined;
|
package/lib/voice/sayAloud.js
CHANGED
|
@@ -18,9 +18,16 @@
|
|
|
18
18
|
export const VOICE_SAY_EVENT = 'voice.say';
|
|
19
19
|
/** The running application's voice, at a run's start. */
|
|
20
20
|
export const VOICE_SETTINGS_EVENT = 'voice.settings';
|
|
21
|
+
/**
|
|
22
|
+
* The person spoke over the agent, the runtime says (VO-46): a client
|
|
23
|
+
* playing its voice stops it.
|
|
24
|
+
*/
|
|
25
|
+
export const VOICE_INTERRUPTED_EVENT = 'voice.interrupted';
|
|
21
26
|
/** The longest line said aloud: the runtime refuses longer ones. */
|
|
22
27
|
export const SAY_ALOUD_MAX_CHARACTERS = 200;
|
|
23
28
|
const nameOf = (type) => type.replace(/^data-/, '');
|
|
29
|
+
/** Whether an activity is the runtime's `voice.interrupted` (VO-46). */
|
|
30
|
+
export const isVoiceInterrupted = (activity) => nameOf(activity.type) === VOICE_INTERRUPTED_EVENT;
|
|
24
31
|
/** The line a `voice.say` says, or `undefined` for anything else. */
|
|
25
32
|
export function lineToSay(activity) {
|
|
26
33
|
if (nameOf(activity.type) !== VOICE_SAY_EVENT) {
|