@datalayer/agent-runtimes 1.5.16 → 1.5.18
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-Bz0eyWo6.js +32 -0
- package/dist-embed/datalayer-app-main.js +102 -102
- package/lib/apps/apps/appspec.d.ts +36 -4
- package/lib/apps/apps/appspec.js +104 -11
- package/lib/apps/apps/threads.d.ts +20 -0
- package/lib/apps/apps/threads.js +31 -0
- package/lib/apps/plugins/canvas-blocks/index.d.ts +7 -9
- package/lib/apps/plugins/canvas-blocks/index.js +8 -10
- package/lib/apps/plugins/canvas-parts/index.d.ts +4 -1
- package/lib/apps/plugins/canvas-parts/index.js +7 -3
- package/lib/apps/plugins/chat/ChatView.js +32 -7
- package/lib/chat/ChatFloating.js +12 -4
- package/lib/chat/base/ChatBase.js +31 -5
- package/lib/chat/messages/ChatMessageList.js +1 -1
- package/lib/config/AgentConfiguration.d.ts +5 -1
- package/lib/examples/AgentA2ATeamExample.js +6 -5
- package/lib/examples/ChatExample.js +2 -2
- package/lib/examples/VoiceChatExample.js +9 -3
- package/lib/hooks/useBrowserAgent.d.ts +1 -1
- package/lib/hooks/useBrowserAgent.js +1 -1
- package/lib/protocols/AGUIAdapter.d.ts +11 -0
- package/lib/protocols/AGUIAdapter.js +27 -2
- package/lib/specs/agents/agents.js +169 -169
- package/lib/specs/apps.d.ts +32 -32
- package/lib/specs/apps.js +624 -143
- package/lib/specs/appspecSchema.js +30 -2
- package/lib/specs/cogs.js +3 -3
- package/lib/specs/index.d.ts +1 -0
- package/lib/specs/index.js +1 -0
- package/lib/specs/protocols.js +2 -2
- package/lib/specs/sceneSchema.js +2 -0
- package/lib/specs/scenes.js +7 -7
- package/lib/specs/teams/teams.js +11 -11
- package/lib/specs/ui.d.ts +32 -6
- package/lib/specs/ui.js +760 -26
- package/lib/specs/uiRenderers.d.ts +17 -0
- package/lib/specs/uiRenderers.js +40 -0
- package/lib/specs/voices.d.ts +2 -0
- package/lib/specs/voices.js +5 -0
- package/lib/types/agentspecs.d.ts +63 -12
- package/lib/types/chat.d.ts +6 -0
- package/lib/types/protocol.d.ts +17 -0
- package/lib/utils/utils.js +8 -1
- package/lib/voice/ReadAloud.d.ts +35 -0
- package/lib/voice/ReadAloud.js +29 -0
- package/lib/voice/index.d.ts +7 -2
- package/lib/voice/index.js +1 -0
- package/package.json +2 -2
- package/scripts/codegen/generate_agents.py +60 -11
- package/scripts/codegen/generate_ui.py +68 -22
- package/scripts/codegen/generate_ui_renderers.py +176 -0
- package/scripts/codegen/generate_voices.py +5 -1
- package/dist-embed/chunks/ChatView-nnI1-MOR.js +0 -32
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
*
|
|
25
25
|
* @module apps/apps/appspec
|
|
26
26
|
*/
|
|
27
|
-
import type { AppAccent, AppThemeMode, AppThemeVariant, AppAssistantCharacter, AppEmbedMode, PapProfile, AppCustomComponentSpec, AppVoiceSpec, AppKind, AppLayout, AppPageOutputComponent, AppSpec } from '../../types/agentspecs';
|
|
27
|
+
import type { AppAccent, AppThemeMode, AppThemeVariant, AppAssistantCharacter, AppEmbedMode, PapProfile, AppCustomComponentSpec, AppVoiceSpec, AppKind, AppLayout, AppPageOutputComponent, AppSpec, UiChoice } from '../../types/agentspecs';
|
|
28
28
|
/** The version of the spec itself. */
|
|
29
29
|
export declare const APP_SCHEMA = "loop.app/v1";
|
|
30
30
|
/** The language an application's own words are in, unless it says (LOOP P-26). */
|
|
@@ -53,6 +53,36 @@ export declare const APP_ASSISTANT_CHARACTER_ID: RegExp;
|
|
|
53
53
|
export declare function isAssistantCharacterId(value: unknown): value is AppAssistantCharacter;
|
|
54
54
|
/** The layout a kind starts with. */
|
|
55
55
|
export declare const DEFAULT_LAYOUTS: Record<AppKind, AppLayout>;
|
|
56
|
+
/** The media type of an answer drawn as A2UI surfaces. */
|
|
57
|
+
export declare const A2UI_OUTPUT = "application/json+a2ui";
|
|
58
|
+
/** What `uiFor` reads of an application besides its kind. */
|
|
59
|
+
export interface AppUiFacts {
|
|
60
|
+
/** Its layout; its kind's own when unsaid. */
|
|
61
|
+
layout?: AppLayout;
|
|
62
|
+
/** The media types it answers in. */
|
|
63
|
+
outputs?: readonly string[];
|
|
64
|
+
/** It signs people in at companies (`pap.connections`). */
|
|
65
|
+
signsIn?: boolean;
|
|
66
|
+
/** It has components of its own (`interface.customComponents`). */
|
|
67
|
+
ownComponents?: boolean;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The UI plugins an application is drawn with (AGENTIC_UI R8), as its `ui`
|
|
71
|
+
* lists them: what `appPreset` mounts for it. A decision: its page alone,
|
|
72
|
+
* drawn by its host. Every other kind: the conversation — `chat`, which
|
|
73
|
+
* stands for the conversation base the runtime mounts — then the A2UI
|
|
74
|
+
* surfaces when it answers in `application/json+a2ui`, the sign-in card when
|
|
75
|
+
* it signs people in at companies, its page, its layout and the Canvas's
|
|
76
|
+
* blocks when its layout is not `chat`, and its own components when it has
|
|
77
|
+
* some. What a host adds (the Preview's sidebar, a frame) is not the
|
|
78
|
+
* application's.
|
|
79
|
+
*
|
|
80
|
+
* agentspecs' `ui_for` is the same function: both are held to
|
|
81
|
+
* `agent_runtimes/tests/fixtures/app_ui.json`.
|
|
82
|
+
*/
|
|
83
|
+
export declare function uiFor(kind: AppKind, facts?: AppUiFacts): UiChoice[];
|
|
84
|
+
/** The UI plugins an application is drawn with, by what its spec says (`uiFor`). */
|
|
85
|
+
export declare function uiOf(app: Pick<AppSpec, 'kind' | 'interface' | 'pap'>): UiChoice[];
|
|
56
86
|
/** What an application keeps of what it did, when it does not say. */
|
|
57
87
|
export declare const DEFAULT_RECORD_INCLUDE: string[];
|
|
58
88
|
type Data = Record<string, unknown>;
|
|
@@ -97,9 +127,11 @@ export interface ParsedAppspec {
|
|
|
97
127
|
*/
|
|
98
128
|
export declare function parseAppspec(document: unknown): ParsedAppspec;
|
|
99
129
|
/**
|
|
100
|
-
* What is wrong with the UI an application
|
|
101
|
-
*
|
|
102
|
-
*
|
|
130
|
+
* What is wrong with the UI plugins an application names (AGENTIC_UI R8), in
|
|
131
|
+
* the sentences agentspecs says (`ui_choices_problems`): a plugin the
|
|
132
|
+
* catalogue does not have or said twice — a renderer named as a plugin is
|
|
133
|
+
* refused —, a renderer the catalogue does not have or the plugin may not be
|
|
134
|
+
* exposed through, and one not enabled while the application is offered.
|
|
103
135
|
*/
|
|
104
136
|
export declare function uiProblems(app: Pick<AppSpec, 'ui' | 'enabled'>): string[];
|
|
105
137
|
/**
|
package/lib/apps/apps/appspec.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { BEHAVIOURS } from './rules';
|
|
6
6
|
import { PROTOCOL_CATALOGUE } from '../../specs/protocols';
|
|
7
7
|
import { UI_CATALOGUE } from '../../specs/ui';
|
|
8
|
+
import { UI_RENDERER_CATALOGUE } from '../../specs/uiRenderers';
|
|
8
9
|
import { DEFAULT_MAX_FILES, DEFAULT_UPLOAD_MB } from './uploads';
|
|
9
10
|
/** The version of the spec itself. */
|
|
10
11
|
export const APP_SCHEMA = 'loop.app/v1';
|
|
@@ -73,6 +74,50 @@ export const DEFAULT_LAYOUTS = {
|
|
|
73
74
|
decision: 'page',
|
|
74
75
|
worker: 'split',
|
|
75
76
|
};
|
|
77
|
+
/** The media type of an answer drawn as A2UI surfaces. */
|
|
78
|
+
export const A2UI_OUTPUT = 'application/json+a2ui';
|
|
79
|
+
/**
|
|
80
|
+
* The UI plugins an application is drawn with (AGENTIC_UI R8), as its `ui`
|
|
81
|
+
* lists them: what `appPreset` mounts for it. A decision: its page alone,
|
|
82
|
+
* drawn by its host. Every other kind: the conversation — `chat`, which
|
|
83
|
+
* stands for the conversation base the runtime mounts — then the A2UI
|
|
84
|
+
* surfaces when it answers in `application/json+a2ui`, the sign-in card when
|
|
85
|
+
* it signs people in at companies, its page, its layout and the Canvas's
|
|
86
|
+
* blocks when its layout is not `chat`, and its own components when it has
|
|
87
|
+
* some. What a host adds (the Preview's sidebar, a frame) is not the
|
|
88
|
+
* application's.
|
|
89
|
+
*
|
|
90
|
+
* agentspecs' `ui_for` is the same function: both are held to
|
|
91
|
+
* `agent_runtimes/tests/fixtures/app_ui.json`.
|
|
92
|
+
*/
|
|
93
|
+
export function uiFor(kind, facts = {}) {
|
|
94
|
+
if (kind === 'decision') {
|
|
95
|
+
return [{ plugin: 'app-page' }];
|
|
96
|
+
}
|
|
97
|
+
const drawn = [{ plugin: 'chat' }];
|
|
98
|
+
if (facts.outputs?.includes(A2UI_OUTPUT)) {
|
|
99
|
+
drawn.push({ plugin: 'a2ui-surface', renderer: 'a2ui' });
|
|
100
|
+
}
|
|
101
|
+
if (facts.signsIn) {
|
|
102
|
+
drawn.push({ plugin: 'pap-sign-in' });
|
|
103
|
+
}
|
|
104
|
+
if ((facts.layout ?? DEFAULT_LAYOUTS[kind]) !== 'chat') {
|
|
105
|
+
drawn.push({ plugin: 'page-layout' }, { plugin: 'app-page' }, { plugin: 'canvas-blocks' });
|
|
106
|
+
}
|
|
107
|
+
if (facts.ownComponents) {
|
|
108
|
+
drawn.push({ plugin: 'app-components' });
|
|
109
|
+
}
|
|
110
|
+
return drawn;
|
|
111
|
+
}
|
|
112
|
+
/** The UI plugins an application is drawn with, by what its spec says (`uiFor`). */
|
|
113
|
+
export function uiOf(app) {
|
|
114
|
+
return uiFor(app.kind, {
|
|
115
|
+
layout: app.interface.layout,
|
|
116
|
+
outputs: app.interface.outputs,
|
|
117
|
+
signsIn: Boolean(app.pap?.connections.length),
|
|
118
|
+
ownComponents: Boolean(app.interface.customComponents?.length),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
76
121
|
/** What an application keeps of what it did, when it does not say. */
|
|
77
122
|
export const DEFAULT_RECORD_INCLUDE = ['conversations', 'actions', 'approvals'];
|
|
78
123
|
const DEFAULT_KEEP_FOR = '1_years';
|
|
@@ -90,6 +135,30 @@ const texts = (value) => Array.isArray(value)
|
|
|
90
135
|
? value.filter((item) => typeof item === 'string')
|
|
91
136
|
: [];
|
|
92
137
|
const records = (value) => Array.isArray(value) ? value.filter(isData) : [];
|
|
138
|
+
/**
|
|
139
|
+
* An application's UI plugins as its document says them (AGENTIC_UI R8):
|
|
140
|
+
* `{plugin, renderer}` each. Anything else — a bare id, the old shape — is
|
|
141
|
+
* not read as a plugin: `parseAppspec` refuses it (`uiShapeProblems`).
|
|
142
|
+
*/
|
|
143
|
+
const uiChoices = (value) => Array.isArray(value)
|
|
144
|
+
? value.filter(isData).map(item => ({
|
|
145
|
+
plugin: text(item.plugin),
|
|
146
|
+
...(text(item.renderer) ? { renderer: text(item.renderer) } : {}),
|
|
147
|
+
}))
|
|
148
|
+
: [];
|
|
149
|
+
/**
|
|
150
|
+
* An entry of `ui` that is not `{plugin, renderer}`, in agentspecs' sentence
|
|
151
|
+
* (`ui_choices_problems`): a bare id is refused, never read as the plugin it
|
|
152
|
+
* names, and one naming a renderer says how to write it.
|
|
153
|
+
*/
|
|
154
|
+
const uiShapeProblems = (value) => Array.isArray(value)
|
|
155
|
+
? value
|
|
156
|
+
.filter(item => !isData(item))
|
|
157
|
+
.map(item => `\`ui\` lists UI plugins, each as \`{plugin, renderer}\`: ${JSON.stringify(item)} is not one` +
|
|
158
|
+
(typeof item === 'string' && UI_RENDERER_CATALOGUE[item]
|
|
159
|
+
? ` (${JSON.stringify(item)} is a UI renderer: name the plugin it renders, and say it as its \`renderer\`).`
|
|
160
|
+
: '.'))
|
|
161
|
+
: [];
|
|
93
162
|
/**
|
|
94
163
|
* One turn of a test's conversation as its document says it (STUDIO E-01):
|
|
95
164
|
* what it says, chooses or presses, with a press's payload; nothing it does
|
|
@@ -133,7 +202,8 @@ export function emptyAppspec(kind = 'chat') {
|
|
|
133
202
|
contents: [],
|
|
134
203
|
connections: [],
|
|
135
204
|
protocols: [],
|
|
136
|
-
|
|
205
|
+
// A blank application starts with its kind's UI plugins (AGENTIC_UI R8).
|
|
206
|
+
ui: uiFor(kind),
|
|
137
207
|
rules: [],
|
|
138
208
|
permissions: {
|
|
139
209
|
spaces: [],
|
|
@@ -937,6 +1007,7 @@ export function parseAppspec(document) {
|
|
|
937
1007
|
problems.push(`\`${key}\` is not a field of the spec.`);
|
|
938
1008
|
}
|
|
939
1009
|
}
|
|
1010
|
+
problems.push(...uiShapeProblems(data.ui));
|
|
940
1011
|
const schema = text(data.schema, APP_SCHEMA);
|
|
941
1012
|
if (schema !== APP_SCHEMA) {
|
|
942
1013
|
problems.push(`This reads ${APP_SCHEMA}; the application is written in ${schema}.`);
|
|
@@ -984,7 +1055,7 @@ export function parseAppspec(document) {
|
|
|
984
1055
|
contents: texts(data.contents),
|
|
985
1056
|
connections: records(data.connections).map(parseConnection),
|
|
986
1057
|
protocols: texts(data.protocols),
|
|
987
|
-
ui:
|
|
1058
|
+
ui: uiChoices(data.ui),
|
|
988
1059
|
rules: records(data.rules).map(parseRule),
|
|
989
1060
|
permissions: {
|
|
990
1061
|
spaces: records(permissions.spaces).map(grant => ({
|
|
@@ -1097,25 +1168,45 @@ export function parseAppspec(document) {
|
|
|
1097
1168
|
return { app, problems };
|
|
1098
1169
|
}
|
|
1099
1170
|
/**
|
|
1100
|
-
* What is wrong with the UI an application
|
|
1101
|
-
*
|
|
1102
|
-
*
|
|
1171
|
+
* What is wrong with the UI plugins an application names (AGENTIC_UI R8), in
|
|
1172
|
+
* the sentences agentspecs says (`ui_choices_problems`): a plugin the
|
|
1173
|
+
* catalogue does not have or said twice — a renderer named as a plugin is
|
|
1174
|
+
* refused —, a renderer the catalogue does not have or the plugin may not be
|
|
1175
|
+
* exposed through, and one not enabled while the application is offered.
|
|
1103
1176
|
*/
|
|
1104
1177
|
export function uiProblems(app) {
|
|
1105
1178
|
const problems = [];
|
|
1106
1179
|
const seen = [];
|
|
1107
|
-
for (const
|
|
1180
|
+
for (const choice of app.ui) {
|
|
1181
|
+
const id = choice.plugin;
|
|
1108
1182
|
if (seen.includes(id)) {
|
|
1109
|
-
problems.push(`It lists the UI '${id}' twice.`);
|
|
1183
|
+
problems.push(`It lists the UI plugin '${id}' twice.`);
|
|
1110
1184
|
continue;
|
|
1111
1185
|
}
|
|
1112
1186
|
seen.push(id);
|
|
1113
1187
|
const spec = UI_CATALOGUE[id];
|
|
1114
1188
|
if (!spec) {
|
|
1115
|
-
problems.push(`There is no UI named '${id}': one of ${Object.keys(UI_CATALOGUE).sort().join(', ')}.`
|
|
1189
|
+
problems.push(`There is no UI plugin named '${id}': one of ${Object.keys(UI_CATALOGUE).sort().join(', ')}.` +
|
|
1190
|
+
(UI_RENDERER_CATALOGUE[id]
|
|
1191
|
+
? ` '${id}' is a UI renderer: say it as a plugin's \`renderer\`.`
|
|
1192
|
+
: ''));
|
|
1193
|
+
continue;
|
|
1194
|
+
}
|
|
1195
|
+
if (choice.renderer === undefined) {
|
|
1196
|
+
continue;
|
|
1197
|
+
}
|
|
1198
|
+
const renderer = UI_RENDERER_CATALOGUE[choice.renderer];
|
|
1199
|
+
if (!renderer) {
|
|
1200
|
+
problems.push(`There is no UI renderer named '${choice.renderer}': one of ${Object.keys(UI_RENDERER_CATALOGUE).sort().join(', ')}.`);
|
|
1201
|
+
}
|
|
1202
|
+
else if (!spec.renderers.includes(renderer.id)) {
|
|
1203
|
+
problems.push(`The UI plugin ${spec.name} ('${id}') is not exposed through ${renderer.name}: ` +
|
|
1204
|
+
(spec.renderers.length > 0
|
|
1205
|
+
? `through ${spec.renderers.join(', ')} only.`
|
|
1206
|
+
: 'through no renderer.'));
|
|
1116
1207
|
}
|
|
1117
|
-
else if (app.enabled && !
|
|
1118
|
-
problems.push(`The UI ${
|
|
1208
|
+
else if (app.enabled && !renderer.enabled) {
|
|
1209
|
+
problems.push(`The UI renderer ${renderer.name} ('${renderer.id}') is not enabled: an application cannot be exposed through it yet.`);
|
|
1119
1210
|
}
|
|
1120
1211
|
}
|
|
1121
1212
|
return problems;
|
|
@@ -1426,7 +1517,9 @@ export function dumpAppspec(app) {
|
|
|
1426
1517
|
.value('as', connection.as, 'owner')
|
|
1427
1518
|
.list('only', connection.only).data))
|
|
1428
1519
|
.list('protocols', app.protocols)
|
|
1429
|
-
.list('ui', app.ui
|
|
1520
|
+
.list('ui', app.ui.map(choice => choice.renderer
|
|
1521
|
+
? { plugin: choice.plugin, renderer: choice.renderer }
|
|
1522
|
+
: { plugin: choice.plugin }))
|
|
1430
1523
|
.part('pap', dumpPap(app.pap))
|
|
1431
1524
|
.part('pact', dumpPact(app.pact))
|
|
1432
1525
|
.part('commerce', dumpCommerce(app.commerce))
|
|
@@ -13,12 +13,32 @@
|
|
|
13
13
|
*
|
|
14
14
|
* @module apps/apps/threads
|
|
15
15
|
*/
|
|
16
|
+
import type { ResumedThread } from '../../types/chat';
|
|
16
17
|
import type { ChatMessage } from '../../types/messages';
|
|
17
18
|
/** What opening a thread says, in a person's words. */
|
|
18
19
|
export declare const THREAD_WORDS: {
|
|
19
20
|
readonly opening: "Opening your conversation…";
|
|
20
21
|
readonly notOpened: (detail: string) => string;
|
|
21
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* The deployment an application's agent is created for, as its create
|
|
25
|
+
* payload names it (`app_instance.deployment_uid`, `appDatalayerCreatePayload`),
|
|
26
|
+
* or `''` for a Preview, a test or no application.
|
|
27
|
+
*/
|
|
28
|
+
export declare function deploymentOfPayload(createPayload: Record<string, unknown> | undefined): string;
|
|
29
|
+
/**
|
|
30
|
+
* The thread a deployment's chat opens on when its host gave none: a new one,
|
|
31
|
+
* `id` (PAP_DATALAYER's release gates, found on r1, 14 October 2026).
|
|
32
|
+
*
|
|
33
|
+
* A deployment's agent serves every visitor, and what its runtime holds in
|
|
34
|
+
* memory is the last run's, whoever ran it: drawn as the page's
|
|
35
|
+
* conversation, a second account opening the same link saw the first
|
|
36
|
+
* person's turns. A given thread is drawn as given and never replaced by the
|
|
37
|
+
* runtime's snapshot, so each visitor sees only their own conversation —
|
|
38
|
+
* this one, or one opened from their history. A Preview, or a chat that runs
|
|
39
|
+
* no deployment, keeps what it was given.
|
|
40
|
+
*/
|
|
41
|
+
export declare function threadOfVisit(given: ResumedThread | undefined, deploymentUid: string, id: string): ResumedThread | undefined;
|
|
22
42
|
/** The AG-UI messages a session-API stream's `MESSAGES_SNAPSHOT` carries, if one does. */
|
|
23
43
|
export declare function snapshotOfStream(text: string): unknown[] | undefined;
|
|
24
44
|
/**
|
package/lib/apps/apps/threads.js
CHANGED
|
@@ -8,6 +8,37 @@ export const THREAD_WORDS = {
|
|
|
8
8
|
opening: 'Opening your conversation…',
|
|
9
9
|
notOpened: (detail) => `Your conversation could not be opened, so this is a new one: ${detail}`,
|
|
10
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* The deployment an application's agent is created for, as its create
|
|
13
|
+
* payload names it (`app_instance.deployment_uid`, `appDatalayerCreatePayload`),
|
|
14
|
+
* or `''` for a Preview, a test or no application.
|
|
15
|
+
*/
|
|
16
|
+
export function deploymentOfPayload(createPayload) {
|
|
17
|
+
const instance = createPayload?.app_instance;
|
|
18
|
+
if (!instance || typeof instance !== 'object') {
|
|
19
|
+
return '';
|
|
20
|
+
}
|
|
21
|
+
const uid = instance.deployment_uid;
|
|
22
|
+
return typeof uid === 'string' ? uid.trim() : '';
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The thread a deployment's chat opens on when its host gave none: a new one,
|
|
26
|
+
* `id` (PAP_DATALAYER's release gates, found on r1, 14 October 2026).
|
|
27
|
+
*
|
|
28
|
+
* A deployment's agent serves every visitor, and what its runtime holds in
|
|
29
|
+
* memory is the last run's, whoever ran it: drawn as the page's
|
|
30
|
+
* conversation, a second account opening the same link saw the first
|
|
31
|
+
* person's turns. A given thread is drawn as given and never replaced by the
|
|
32
|
+
* runtime's snapshot, so each visitor sees only their own conversation —
|
|
33
|
+
* this one, or one opened from their history. A Preview, or a chat that runs
|
|
34
|
+
* no deployment, keeps what it was given.
|
|
35
|
+
*/
|
|
36
|
+
export function threadOfVisit(given, deploymentUid, id) {
|
|
37
|
+
if (given || !deploymentUid) {
|
|
38
|
+
return given;
|
|
39
|
+
}
|
|
40
|
+
return { id, messages: [] };
|
|
41
|
+
}
|
|
11
42
|
/** The AG-UI messages a session-API stream's `MESSAGES_SNAPSHOT` carries, if one does. */
|
|
12
43
|
export function snapshotOfStream(text) {
|
|
13
44
|
let found;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The Canvas's palette as a
|
|
3
|
-
* the catalogue that renders components (`agentspecs/ui`, C-13
|
|
4
|
-
*
|
|
2
|
+
* The Canvas's palette as a Contribution Point (LOOP C-12): each UI plugin
|
|
3
|
+
* of the catalogue that renders components (`agentspecs/ui`, C-13 — the
|
|
4
|
+
* A2UI surfaces, `a2ui-surface`) has its components contributed to
|
|
5
|
+
* `loop.canvas.block` by a Reactor plugin of its own, one per UI spec. The
|
|
5
6
|
* palette lists what the enabled plugins contribute, and nothing else; a
|
|
6
7
|
* plugin disabled takes its blocks off it, and an application that uses one
|
|
7
8
|
* of them says so in its setup notes (`blockSetupNotes`).
|
|
@@ -24,10 +25,7 @@ type BlocksReader = {
|
|
|
24
25
|
export declare const canvasBlocksPluginName: (ui: string) => string;
|
|
25
26
|
/** A UI of the catalogue as the Reactor plugin contributing its components as blocks. */
|
|
26
27
|
export declare function uiPluginBlocks(ui: UISpec): ReactorPlugin<Record<string, never>, unknown, unknown>;
|
|
27
|
-
/**
|
|
28
|
-
* The plugins of the catalogue's UI specs that render components and may
|
|
29
|
-
* be named today (`enabled` in the catalogue).
|
|
30
|
-
*/
|
|
28
|
+
/** The block plugins of the catalogue's UI plugins that render components. */
|
|
31
29
|
export declare const CANVAS_BLOCK_PLUGINS: ReactorPlugin<Record<string, never>, unknown, unknown>[];
|
|
32
30
|
/**
|
|
33
31
|
* A reactor holding the block plugins given, started, with those named in
|
|
@@ -35,8 +33,8 @@ export declare const CANVAS_BLOCK_PLUGINS: ReactorPlugin<Record<string, never>,
|
|
|
35
33
|
*/
|
|
36
34
|
export declare function canvasBlocksReactor(disabled?: readonly string[], plugins?: ReactorPlugin<any, any, any>[]): ReactorPlatform;
|
|
37
35
|
/**
|
|
38
|
-
* The palette of an organization: the block plugins of the UI
|
|
39
|
-
* turned off (`plugins_off` in IAM,
|
|
36
|
+
* The palette of an organization: the block plugins of the UI plugins it has
|
|
37
|
+
* turned off (`plugins_off` in IAM, ids of `agentspecs/ui` such as 'a2ui-surface') switched
|
|
40
38
|
* off. A UI of the catalogue that contributes no block takes nothing
|
|
41
39
|
* off; an id the catalogue does not have is said by `unknownPluginsOff`.
|
|
42
40
|
*/
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
|
-
* The Canvas's palette as a
|
|
7
|
-
* the catalogue that renders components (`agentspecs/ui`, C-13
|
|
8
|
-
*
|
|
6
|
+
* The Canvas's palette as a Contribution Point (LOOP C-12): each UI plugin
|
|
7
|
+
* of the catalogue that renders components (`agentspecs/ui`, C-13 — the
|
|
8
|
+
* A2UI surfaces, `a2ui-surface`) has its components contributed to
|
|
9
|
+
* `loop.canvas.block` by a Reactor plugin of its own, one per UI spec. The
|
|
9
10
|
* palette lists what the enabled plugins contribute, and nothing else; a
|
|
10
11
|
* plugin disabled takes its blocks off it, and an application that uses one
|
|
11
12
|
* of them says so in its setup notes (`blockSetupNotes`).
|
|
@@ -30,12 +31,9 @@ export function uiPluginBlocks(ui) {
|
|
|
30
31
|
contributes: ui.components.map(component => contribution(LoopCanvasBlock, { id: component.id, ui: ui.id, component }, { id: component.id })),
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
|
-
/**
|
|
34
|
-
* The plugins of the catalogue's UI specs that render components and may
|
|
35
|
-
* be named today (`enabled` in the catalogue).
|
|
36
|
-
*/
|
|
34
|
+
/** The block plugins of the catalogue's UI plugins that render components. */
|
|
37
35
|
export const CANVAS_BLOCK_PLUGINS = listUI()
|
|
38
|
-
.filter(ui => ui.
|
|
36
|
+
.filter(ui => ui.components.length > 0)
|
|
39
37
|
.map(uiPluginBlocks);
|
|
40
38
|
/**
|
|
41
39
|
* A reactor holding the block plugins given, started, with those named in
|
|
@@ -53,8 +51,8 @@ export function canvasBlocksReactor(disabled = [], plugins = CANVAS_BLOCK_PLUGIN
|
|
|
53
51
|
return reactor;
|
|
54
52
|
}
|
|
55
53
|
/**
|
|
56
|
-
* The palette of an organization: the block plugins of the UI
|
|
57
|
-
* turned off (`plugins_off` in IAM,
|
|
54
|
+
* The palette of an organization: the block plugins of the UI plugins it has
|
|
55
|
+
* turned off (`plugins_off` in IAM, ids of `agentspecs/ui` such as 'a2ui-surface') switched
|
|
58
56
|
* off. A UI of the catalogue that contributes no block takes nothing
|
|
59
57
|
* off; an id the catalogue does not have is said by `unknownPluginsOff`.
|
|
60
58
|
*/
|
|
@@ -32,7 +32,10 @@ export declare const TEST_PARTS: CanvasPartContribution[];
|
|
|
32
32
|
export declare const EVENT_PARTS: CanvasPartContribution[];
|
|
33
33
|
/** Each protocol of the catalogue (AGENT_PROTOCOLS PS-04): a planned one offered, not enabled. */
|
|
34
34
|
export declare const PROTOCOL_PARTS: CanvasPartContribution[];
|
|
35
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Each UI plugin of the catalogue (AGENTIC_UI R8): a part of the agent's
|
|
37
|
+
* interface, dropped on it, then exposed through a renderer it may be.
|
|
38
|
+
*/
|
|
36
39
|
export declare const UI_PARTS: CanvasPartContribution[];
|
|
37
40
|
/** Datalayer's characters for its floating assistant, dropped on the agent (AGENT_PROTOCOLS PS-05). */
|
|
38
41
|
export declare const CHARACTER_PARTS: CanvasPartContribution[];
|
|
@@ -187,14 +187,18 @@ export const PROTOCOL_PARTS = listProtocolSpecs().map(protocol => ({
|
|
|
187
187
|
example: protocol.id,
|
|
188
188
|
enabled: protocol.status === 'shipped',
|
|
189
189
|
}));
|
|
190
|
-
/**
|
|
190
|
+
/**
|
|
191
|
+
* Each UI plugin of the catalogue (AGENTIC_UI R8): a part of the agent's
|
|
192
|
+
* interface, dropped on it, then exposed through a renderer it may be.
|
|
193
|
+
*/
|
|
191
194
|
export const UI_PARTS = listUI().map(ui => ({
|
|
192
195
|
id: `ui:${ui.id}`,
|
|
193
196
|
kind: 'ui',
|
|
194
197
|
label: ui.name,
|
|
195
198
|
says: ui.description,
|
|
196
|
-
|
|
197
|
-
|
|
199
|
+
// The Appspec's `ui` entry it adds: the plugin, its renderer chosen after.
|
|
200
|
+
example: { plugin: ui.id },
|
|
201
|
+
enabled: true,
|
|
198
202
|
}));
|
|
199
203
|
/** Datalayer's characters for its floating assistant, dropped on the agent (AGENT_PROTOCOLS PS-05). */
|
|
200
204
|
export const CHARACTER_PARTS = ASSISTANT_CHARACTERS.map(character => ({
|
|
@@ -48,7 +48,7 @@ import { isInactiveSurfaceContribution, orderToolContributions, toolsForChatView
|
|
|
48
48
|
import { agentBundleToolDefinitions, useAgentCommandTools, } from '../../../tools/adapters/commands';
|
|
49
49
|
import { NONE_EDITOR, seedEditorChoice, setEditorOptions, } from '../shell/editorChoice';
|
|
50
50
|
import { generateMessageId } from '../../../types/messages';
|
|
51
|
-
import { openThread, THREAD_WORDS } from '../../apps/threads';
|
|
51
|
+
import { deploymentOfPayload, openThread, THREAD_WORDS, threadOfVisit, } from '../../apps/threads';
|
|
52
52
|
import { FaceDrawing, PresenceFace, PresenceLine, } from '../../../chat/presence/Presence';
|
|
53
53
|
import { presenceState, presenceToolOf, } from '../../../chat/presence/presenceStatus';
|
|
54
54
|
import { latestSaying, newestIsAnswer, } from '../../../chat/assistant/state';
|
|
@@ -1280,9 +1280,10 @@ export default function ChatView({ workspace }) {
|
|
|
1280
1280
|
* not published (STUDIO D-15). It speaks to the application's session API,
|
|
1281
1281
|
* whose refusal says why.
|
|
1282
1282
|
*/
|
|
1283
|
-
const
|
|
1284
|
-
|
|
1285
|
-
|
|
1283
|
+
const hostCreatePayload = reactor.getConfig(AGENTS_PLUGIN_NAME)?.datalayerCreatePayload;
|
|
1284
|
+
const runsApp = Boolean(blueprintTurn?.createPayload?.app_spec || hostCreatePayload?.app_spec);
|
|
1285
|
+
// The deployment its host runs, if it runs one: its agent is every visitor's.
|
|
1286
|
+
const deploymentUid = deploymentOfPayload(hostCreatePayload);
|
|
1286
1287
|
const memberToken = useIAMStore(state => state.token);
|
|
1287
1288
|
/*
|
|
1288
1289
|
* Without an account (LOOP R-30) the application runs on the visitors'
|
|
@@ -1294,13 +1295,28 @@ export default function ChatView({ workspace }) {
|
|
|
1294
1295
|
// not know (LOOP R-20): it runs a session of its deployment, and no more.
|
|
1295
1296
|
const embedToken = reactor.getConfig(AGENTS_PLUGIN_NAME)?.embedToken;
|
|
1296
1297
|
const iamToken = visitors ? visitor.token : embedToken || memberToken;
|
|
1298
|
+
/*
|
|
1299
|
+
* A deployment's runtime, and the visitors', answer only the application's
|
|
1300
|
+
* own routes (OwnRoutesOnly, PAP_DATALAYER's release gates): its chat calls
|
|
1301
|
+
* nothing else — Stop is its session's, no monitoring stream, no sandbox
|
|
1302
|
+
* socket, no runtime-wide model switch. A Preview runs on the Maker's.
|
|
1303
|
+
*/
|
|
1304
|
+
const ownRoutesOnly = Boolean(deploymentUid || visitors);
|
|
1305
|
+
if (ownRoutesOnly && !runsApp) {
|
|
1306
|
+
throw new Error('A deployment’s page runs its application: its host gave no app_spec.');
|
|
1307
|
+
}
|
|
1297
1308
|
/*
|
|
1298
1309
|
* A thread opened from the person's history (LOOP P-24): its conversation
|
|
1299
1310
|
* read from the runtime once it answers — held there, else resumed from
|
|
1300
1311
|
* its record — then drawn and gone on with on the same thread. One that
|
|
1301
1312
|
* cannot be opened is said, and a new one starts.
|
|
1302
1313
|
*/
|
|
1303
|
-
|
|
1314
|
+
/*
|
|
1315
|
+
* A deployment's visit with no thread given opens a new one: its runtime's
|
|
1316
|
+
* agent holds every visitor's turns, never drawn as this person's.
|
|
1317
|
+
*/
|
|
1318
|
+
const [visitThreadId] = useState(generateMessageId);
|
|
1319
|
+
const givenThread = useMemo(() => threadOfVisit(config?.thread, deploymentUid, visitThreadId), [config?.thread, deploymentUid, visitThreadId]);
|
|
1304
1320
|
const [openedThread, setOpenedThread] = useState(null);
|
|
1305
1321
|
const opening = Boolean(givenThread?.fromRuntime && openedThread?.given !== givenThread.id);
|
|
1306
1322
|
useEffect(() => {
|
|
@@ -1414,6 +1430,9 @@ export default function ChatView({ workspace }) {
|
|
|
1414
1430
|
uiSocket: `${agentServerUrl.replace(/^http/, 'ws')}/api/v1/apps/ui`,
|
|
1415
1431
|
// Its files, uploaded before what names them (RU-6).
|
|
1416
1432
|
uiFiles: `${agentServerUrl}/api/v1/apps/agents/${encodeURIComponent(agentId)}/files`,
|
|
1433
|
+
// Each thread a session: Stop is its session's.
|
|
1434
|
+
appSessions: `${agentServerUrl}/api/v1/apps/sessions`,
|
|
1435
|
+
...(ownRoutesOnly ? { ownRoutesOnly: true } : {}),
|
|
1417
1436
|
}
|
|
1418
1437
|
: {}),
|
|
1419
1438
|
agentId,
|
|
@@ -1436,6 +1455,7 @@ export default function ChatView({ workspace }) {
|
|
|
1436
1455
|
agentServerUrl,
|
|
1437
1456
|
runsApp,
|
|
1438
1457
|
iamToken,
|
|
1458
|
+
ownRoutesOnly,
|
|
1439
1459
|
]);
|
|
1440
1460
|
/*
|
|
1441
1461
|
* The model catalogue, and the builtin tools and skills the agent reports.
|
|
@@ -1550,6 +1570,10 @@ export default function ChatView({ workspace }) {
|
|
|
1550
1570
|
* to agree.
|
|
1551
1571
|
*/
|
|
1552
1572
|
const selectModel = useCallback(async (model) => {
|
|
1573
|
+
if (ownRoutesOnly) {
|
|
1574
|
+
// Runtime-wide, and not the application's own: never offered here.
|
|
1575
|
+
throw new Error('A deployment’s chat does not switch its runtime’s model.');
|
|
1576
|
+
}
|
|
1553
1577
|
setPickedModel(model);
|
|
1554
1578
|
if (inPage || agentServerUrl === undefined) {
|
|
1555
1579
|
return;
|
|
@@ -1559,7 +1583,7 @@ export default function ChatView({ workspace }) {
|
|
|
1559
1583
|
headers: { 'content-type': 'application/json' },
|
|
1560
1584
|
body: JSON.stringify({ model }),
|
|
1561
1585
|
}).catch(() => undefined);
|
|
1562
|
-
}, [agentServerUrl, inPage]);
|
|
1586
|
+
}, [agentServerUrl, inPage, ownRoutesOnly]);
|
|
1563
1587
|
/* `bottom-chat` keeps the prompt in the chat column; `bottom`, the default,
|
|
1564
1588
|
spans the workspace. */
|
|
1565
1589
|
const besideChat = config?.promptPlacement === 'bottom-chat';
|
|
@@ -1654,7 +1678,8 @@ export default function ChatView({ workspace }) {
|
|
|
1654
1678
|
showTokenUsage: chatExtras.showTokenUsage ?? config?.showTokenUsage ?? true,
|
|
1655
1679
|
showContextRing: true,
|
|
1656
1680
|
agentUsage: contextUsage ?? undefined,
|
|
1657
|
-
|
|
1681
|
+
// Nor on a deployment's page: the model is its runtime's, for everyone.
|
|
1682
|
+
showModelSelector: !presence && !ownRoutesOnly,
|
|
1658
1683
|
models: offeredModels,
|
|
1659
1684
|
decisions,
|
|
1660
1685
|
selectedModel: activeModel,
|
package/lib/chat/ChatFloating.js
CHANGED
|
@@ -35,6 +35,7 @@ import { decisionsAskerAt } from './assistant/decisions';
|
|
|
35
35
|
import { DecisionAsk } from './assistant/DecisionAsk';
|
|
36
36
|
import { ServerSpeaker } from '../voice/speaker';
|
|
37
37
|
import { useSpokenAnswers } from '../voice/useSpokenAnswers';
|
|
38
|
+
import { readAloud } from '../voice/ReadAloud';
|
|
38
39
|
import { BalloonApprovalMessage, CONVERSATION_BALLOON_WIDTH, ConversationBalloonClose, balloonTailAt, conversationBalloonHeight, conversationBalloonSx, CURRENT_BALLOON_HEIGHT, peekLine, } from './assistant/ConversationBalloon';
|
|
39
40
|
import { ConversationBalloonHeader, CurrentBalloonBody, } from './assistant/BalloonParts';
|
|
40
41
|
import { DEFAULT_BALLOON_DISPLAY, conversationCount, newestToolLine, toolLineText, } from './assistant/toolLine';
|
|
@@ -165,7 +166,7 @@ export function ChatFloating({ endpoint, protocol: protocolProp, useStore: useSt
|
|
|
165
166
|
const [speakerState, setSpeakerState] = useState({
|
|
166
167
|
speaking: false,
|
|
167
168
|
});
|
|
168
|
-
const speaker = useMemo(() => voice && voice.output
|
|
169
|
+
const speaker = useMemo(() => voice && voice.output !== 'off' && voice.speechUrl
|
|
169
170
|
? new ServerSpeaker({
|
|
170
171
|
speechUrl: voice.speechUrl,
|
|
171
172
|
voice: voice.voice,
|
|
@@ -195,8 +196,15 @@ export function ChatFloating({ endpoint, protocol: protocolProp, useStore: useSt
|
|
|
195
196
|
window.removeEventListener('keydown', unlock);
|
|
196
197
|
};
|
|
197
198
|
}, [speaker]);
|
|
198
|
-
|
|
199
|
+
// `always`: each answer heard as it is written; `on_request`: a *Read
|
|
200
|
+
// aloud* under each (VO-21).
|
|
201
|
+
useSpokenAnswers(voiceItems, chatBusy, !!speaker && voice?.output === 'always' && !speechMuted, speaker);
|
|
199
202
|
const stopSpeaking = useCallback(() => speaker?.stop(), [speaker]);
|
|
203
|
+
const readAnswerAloud = useCallback((answer) => {
|
|
204
|
+
if (speaker) {
|
|
205
|
+
readAloud(speaker, answer);
|
|
206
|
+
}
|
|
207
|
+
}, [speaker]);
|
|
200
208
|
const mouthLevel = useMemo(() => (speaker ? () => speaker.level() : undefined), [speaker]);
|
|
201
209
|
const [arriving, setArriving] = useState(false);
|
|
202
210
|
const [leaving, setLeaving] = useState(false);
|
|
@@ -1057,7 +1065,7 @@ export function ChatFloating({ endpoint, protocol: protocolProp, useStore: useSt
|
|
|
1057
1065
|
...poweredByProps,
|
|
1058
1066
|
}, description: description, onStateUpdate: onStateUpdate, onNewChat: onNewChat, suggestions: suggestions, submitOnSuggestionClick: submitOnSuggestionClick, hideMessagesAfterToolUI: hideMessagesAfterToolUI, avatarConfig: {
|
|
1059
1067
|
showAvatars: true,
|
|
1060
|
-
}, placeholder: "Type a message...", backgroundColor: "canvas.subtle", frontendTools: frontendTools, showModelSelector: showModelSelector, availableModels: availableModels, showToolsMenu: showToolsMenu, showSkillsMenu: showSkillsMenu, showTokenUsage: showTokenUsage, showContextRing: showContextRing, runtimeId: runtimeId, historyEndpoint: historyEndpoint, historyAuthToken: historyAuthToken, pendingPrompt: pendingPrompt ?? suggestionPrompt, inspector: inspector, showInformation: showInformation, onInformationClick: onInformationClick, onToolCallStart: onToolCallStart, onToolCallComplete: onToolCallComplete, showToolApprovalBanner: showToolApprovalBanner, pendingApprovals: pendingApprovals, onApproveApproval: onApproveApproval, onRejectApproval: onRejectApproval, voice: voice, voiceSpeaking: speakerState.speaking, onStopSpeaking: speaker ? stopSpeaking : undefined, ...panelProps, ...assistantChat, onLoadingChange: handleLoadingChange, onDisplayItemsChange: handleDisplayItemsChange, onSendReady: handleSendReady, children: conversationBalloon && currentOpen ? (
|
|
1068
|
+
}, placeholder: "Type a message...", backgroundColor: "canvas.subtle", frontendTools: frontendTools, showModelSelector: showModelSelector, availableModels: availableModels, showToolsMenu: showToolsMenu, showSkillsMenu: showSkillsMenu, showTokenUsage: showTokenUsage, showContextRing: showContextRing, runtimeId: runtimeId, historyEndpoint: historyEndpoint, historyAuthToken: historyAuthToken, pendingPrompt: pendingPrompt ?? suggestionPrompt, inspector: inspector, showInformation: showInformation, onInformationClick: onInformationClick, onToolCallStart: onToolCallStart, onToolCallComplete: onToolCallComplete, showToolApprovalBanner: showToolApprovalBanner, pendingApprovals: pendingApprovals, onApproveApproval: onApproveApproval, onRejectApproval: onRejectApproval, voice: voice, voiceSpeaking: speakerState.speaking, onStopSpeaking: speaker ? stopSpeaking : undefined, onReadAloud: speaker ? readAnswerAloud : undefined, ...panelProps, ...assistantChat, onLoadingChange: handleLoadingChange, onDisplayItemsChange: handleDisplayItemsChange, onSendReady: handleSendReady, children: conversationBalloon && currentOpen ? (
|
|
1061
1069
|
// Current: the one thing said or done now, over the composer.
|
|
1062
1070
|
_jsx(Box, { px: 3, pt: 3, pb: 2, overflow: "hidden", children: _jsx(CurrentBalloonBody, { text: toolSaid ? undefined : (currentWords ?? description), fullText: toolSaid ? undefined : currentWhole, tool: toolSaid, busy: atWork, speaking: answering && atWork, waiting: atWork && !answering }) })) : (children) }))] }));
|
|
1063
1071
|
/*
|
|
@@ -1109,7 +1117,7 @@ export function ChatFloating({ endpoint, protocol: protocolProp, useStore: useSt
|
|
|
1109
1117
|
}
|
|
1110
1118
|
: getPositionStyles(), stageRef: stageRef, onDragStart: stageDrag.onHandlePointerDown, open: isOpen, onToggle: handleToggle, balloon: assistantBalloon, balloonDisplay: balloonDisplay, expandTarget: expandTarget, insist: balloonInsists, onDismiss: dismissAssistant, ownRef: popupRef, mouthLevel: mouthLevel, suggestions: balloonSuggestions, onSuggestion: sendSuggestion, inspector: inspector, onBalloonDisplayChange: changeBalloonDisplay, onStop: chatBusy && chatControls.current
|
|
1111
1119
|
? () => chatControls.current?.stop()
|
|
1112
|
-
: undefined, onNewChat: !conversation && showNewChatButton ? handleNewChat : undefined, onClear: !conversation && useStoreMode ? handleClear : undefined, onChangeCharacter: onChangeCharacter, speech: speaker
|
|
1120
|
+
: undefined, onNewChat: !conversation && showNewChatButton ? handleNewChat : undefined, onClear: !conversation && useStoreMode ? handleClear : undefined, onChangeCharacter: onChangeCharacter, speech: speaker && voice?.output === 'always'
|
|
1113
1121
|
? {
|
|
1114
1122
|
muted: speechMuted,
|
|
1115
1123
|
onToggle: () => {
|