@datalayer/agent-runtimes 1.3.81 → 1.3.83
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/lib/chat/assistant/SpeechBalloon.js +16 -1
- package/lib/chat/base/ChatBase.js +40 -4
- package/lib/chat/indicators/McpStatusIndicator.d.ts +1 -1
- package/lib/chat/indicators/McpStatusIndicator.js +13 -6
- package/lib/chat/indicators/SkillsStatusIndicator.d.ts +1 -1
- package/lib/chat/indicators/SkillsStatusIndicator.js +7 -3
- package/lib/chat/prompt/InputPrompt.d.ts +4 -1
- package/lib/chat/prompt/InputPrompt.js +2 -2
- package/lib/chat/prompt/InputPromptBase.d.ts +4 -1
- package/lib/chat/prompt/InputPromptBase.js +2 -2
- package/lib/chat/prompt/InputPromptLexical.d.ts +7 -1
- package/lib/chat/prompt/InputPromptLexical.js +5 -3
- package/lib/components/a2ui/datalayer/Form.js +19 -2
- package/lib/components/teams/index.d.ts +1 -0
- package/lib/components/teams/index.js +1 -0
- package/lib/components/teams/usePeerSandbox.d.ts +13 -0
- package/lib/components/teams/usePeerSandbox.js +59 -0
- package/lib/examples/AgentA2ATeamExample.js +7 -2
- package/lib/loop/apps/AppComposer.d.ts +31 -0
- package/lib/loop/apps/AppComposer.js +67 -0
- package/lib/loop/apps/AppRenderer.d.ts +16 -5
- package/lib/loop/apps/AppRenderer.js +49 -16
- package/lib/loop/apps/appspec.d.ts +8 -1
- package/lib/loop/apps/appspec.js +88 -23
- package/lib/loop/apps/checks.js +125 -8
- package/lib/loop/apps/composer.d.ts +37 -0
- package/lib/loop/apps/composer.js +61 -0
- package/lib/loop/apps/index.d.ts +2 -0
- package/lib/loop/apps/index.js +2 -0
- package/lib/loop/core/index.d.ts +31 -0
- package/lib/loop/core/index.js +22 -0
- package/lib/loop/embed/AppEmbed.d.ts +46 -7
- package/lib/loop/embed/AppEmbed.js +108 -26
- package/lib/loop/embed/LoopEmbed.js +2 -0
- package/lib/loop/embed/element.js +19 -5
- package/lib/loop/embed/embedConfig.d.ts +10 -1
- package/lib/loop/embed/embedConfig.js +12 -1
- package/lib/loop/embed/embedSession.d.ts +79 -0
- package/lib/loop/embed/embedSession.js +136 -0
- package/lib/loop/embed/embedTheme.d.ts +12 -9
- package/lib/loop/embed/embedTheme.js +32 -22
- package/lib/loop/index.d.ts +4 -3
- package/lib/loop/index.js +4 -3
- package/lib/loop/plugins/agent-capacity/index.d.ts +3 -1
- package/lib/loop/plugins/agent-capacity/index.js +2 -1
- package/lib/loop/plugins/app-page/appPageModel.d.ts +14 -5
- package/lib/loop/plugins/app-page/appPageModel.js +80 -88
- package/lib/loop/plugins/chat/ChatView.js +31 -4
- package/lib/loop/plugins/chat/index.d.ts +6 -1
- package/lib/loop/presets.d.ts +3 -1
- package/lib/loop/presets.js +2 -1
- package/lib/loop/shell/LoopWorkspace.js +3 -0
- package/lib/protocols/BrowserAgentAdapter.d.ts +18 -0
- package/lib/protocols/BrowserAgentAdapter.js +35 -2
- package/lib/runtimes/__tests__/fixtures/accounting-agent-card.json +8 -0
- package/lib/runtimes/browser/a2aPeer.d.ts +11 -0
- package/lib/runtimes/browser/a2aPeer.js +15 -1
- package/lib/runtimes/browser/protocol.d.ts +3 -0
- package/lib/runtimes/browser/protocol.js +2 -1
- package/lib/specs/agents/agents.js +6 -3
- package/lib/specs/apps.js +216 -176
- package/lib/specs/appspecSchema.js +205 -84
- package/lib/specs/cogs.js +3 -2
- package/lib/types/agentspecs.d.ts +92 -7
- package/lib/types/chat.d.ts +29 -1
- package/package.json +1 -1
- package/scripts/codegen/generate_agents.py +4 -2
- package/scripts/codegen/generate_apps.py +0 -1
- package/scripts/codegen/generate_backend_tools.py +4 -12
- package/scripts/codegen/generate_cogs.py +3 -1
- package/scripts/codegen/generate_ui_plugins.py +22 -9
- package/scripts/voice/transcribe.mjs +29 -21
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { ActionList, ActionMenu } from '@primer/react';
|
|
3
|
+
import { contribution, definePlugin, signal } from '@datalayer/reactor';
|
|
4
|
+
import { useSignalValue } from '@datalayer/reactor/react';
|
|
5
|
+
import { LoopCommand, LoopRunProps, LoopSlots } from '../core';
|
|
6
|
+
import { commandPrompt, modeChoice, modeDefaults } from './composer';
|
|
7
|
+
/** The plugin's name, before the application's id. */
|
|
8
|
+
export const APP_COMPOSER_PLUGIN_NAME = '@datalayer/loop-plugin-app-composer';
|
|
9
|
+
/**
|
|
10
|
+
* The plugin that puts an application's commands in the composer's `/` menu
|
|
11
|
+
* and its modes beside the composer. Nothing for an application with
|
|
12
|
+
* neither: its plugin contributes nothing.
|
|
13
|
+
*/
|
|
14
|
+
export function defineAppComposerPlugin(app) {
|
|
15
|
+
const modes = app.interface.modes ?? [];
|
|
16
|
+
/** The option of each mode the person is in. */
|
|
17
|
+
const chosen = signal(modeDefaults(app));
|
|
18
|
+
function ModeSwitches() {
|
|
19
|
+
const now = useSignalValue(chosen);
|
|
20
|
+
return (_jsx(_Fragment, { children: modes.map(mode => {
|
|
21
|
+
const on = mode.options.find(option => option.id === now[mode.id]) ??
|
|
22
|
+
mode.options[0];
|
|
23
|
+
return (_jsxs(ActionMenu, { children: [_jsxs(ActionMenu.Button, { size: "small", variant: "invisible", "aria-label": `${mode.label}: ${on.label}`, children: [mode.label, ": ", on.label] }), _jsx(ActionMenu.Overlay, { width: "medium", children: _jsx(ActionList, { selectionVariant: "single", children: _jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: mode.label }), mode.options.map(option => (_jsxs(ActionList.Item, { selected: option.id === on.id, onSelect: () => {
|
|
24
|
+
chosen.value = modeChoice(app, {
|
|
25
|
+
...chosen.peek(),
|
|
26
|
+
[mode.id]: option.id,
|
|
27
|
+
});
|
|
28
|
+
}, children: [option.label, option.description ? (_jsx(ActionList.Description, { variant: "block", children: option.description })) : null] }, option.id)))] }) }) })] }, mode.id));
|
|
29
|
+
}) }));
|
|
30
|
+
}
|
|
31
|
+
return definePlugin({
|
|
32
|
+
name: `${APP_COMPOSER_PLUGIN_NAME}-${app.id}`,
|
|
33
|
+
displayName: `${app.name}: commands and modes`,
|
|
34
|
+
description: `The commands and modes ${app.name} offers in its composer.`,
|
|
35
|
+
octicon: 'command-palette',
|
|
36
|
+
emoji: '\u{2318}',
|
|
37
|
+
contributes: [
|
|
38
|
+
...(app.interface.commands ?? []).map(command => contribution(LoopCommand, {
|
|
39
|
+
name: command.name,
|
|
40
|
+
description: command.description,
|
|
41
|
+
group: app.name,
|
|
42
|
+
composer: true,
|
|
43
|
+
run: async ({ argv }) => ({ prompt: commandPrompt(command, argv) }),
|
|
44
|
+
}, { id: `app-command-${command.name}` })),
|
|
45
|
+
...(modes.length > 0
|
|
46
|
+
? [
|
|
47
|
+
contribution(LoopRunProps, {
|
|
48
|
+
id: 'app-modes',
|
|
49
|
+
props: () => ({ loop: { modes: { ...chosen.peek() } } }),
|
|
50
|
+
}, { id: 'app-modes' }),
|
|
51
|
+
]
|
|
52
|
+
: []),
|
|
53
|
+
],
|
|
54
|
+
build: () => ({
|
|
55
|
+
components: modes.length > 0
|
|
56
|
+
? [
|
|
57
|
+
{
|
|
58
|
+
id: 'app-modes',
|
|
59
|
+
slot: LoopSlots.promptAction,
|
|
60
|
+
order: 5,
|
|
61
|
+
Component: ModeSwitches,
|
|
62
|
+
},
|
|
63
|
+
]
|
|
64
|
+
: [],
|
|
65
|
+
}),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -108,6 +108,12 @@ export type AppRendererProps = Omit<LoopEmbedProps, 'agentId'> & {
|
|
|
108
108
|
* `instance.appUid`.
|
|
109
109
|
*/
|
|
110
110
|
sidebar?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Draw its computer alone beside its page (R-23): what a deployment's
|
|
113
|
+
* owner and the editors of its application see of it at its address — its
|
|
114
|
+
* runtime shows it to them only (decided 2026-10-06). Implied by `sidebar`.
|
|
115
|
+
*/
|
|
116
|
+
computer?: boolean;
|
|
111
117
|
/**
|
|
112
118
|
* Draw the application in a window (the `window-frame` plugin), its title
|
|
113
119
|
* the application's face and name. A host's own `frameTitle` wins.
|
|
@@ -157,11 +163,13 @@ export type AppPreset = {
|
|
|
157
163
|
* its page (`app-page`) when it has one, with the Canvas's block plugins
|
|
158
164
|
* but those of the UI plugins its organization turned off (`pluginsOff`),
|
|
159
165
|
* whose contributions are the blocks it may draw (R-01b); a thumb and a
|
|
160
|
-
* comment on each answer when its record keeps feedback (V-18);
|
|
166
|
+
* comment on each answer when its record keeps feedback (V-18); its
|
|
167
|
+
* commands in the composer's `/` menu and its modes beside it (P-19); what it
|
|
161
168
|
* keeps and for how long, under its prompt before the first message, when
|
|
162
169
|
* `kept` says it (R-31); with
|
|
163
170
|
* `sidebar`, its rules and approvals card, its activity feed and its
|
|
164
|
-
* computer (R-01b, R-23);
|
|
171
|
+
* computer (R-01b, R-23); with `computer`, its computer alone — at its
|
|
172
|
+
* address, for its owner and the editors of its application;
|
|
165
173
|
* laid out as `interface.layout` says;
|
|
166
174
|
* - a **decision**: the page its host draws (`page`), as the one view of a
|
|
167
175
|
* workspace without a conversation (R-02).
|
|
@@ -174,6 +182,8 @@ export declare function appPreset(app: AppSpec, options?: {
|
|
|
174
182
|
pluginsOff?: readonly string[];
|
|
175
183
|
/** Its rules and approvals, its activity and its computer, beside its page. */
|
|
176
184
|
sidebar?: boolean;
|
|
185
|
+
/** Its computer alone beside its page (R-23): for its owner and editors at its address. */
|
|
186
|
+
computer?: boolean;
|
|
177
187
|
/** What its record is kept under: its activity's. */
|
|
178
188
|
appUid?: string;
|
|
179
189
|
/**
|
|
@@ -207,8 +217,9 @@ export declare const DATALAYER_BOOTSTRAP_AGENTSPEC = "example-simple";
|
|
|
207
217
|
* The application's accent over the theme its conversation wears, in both
|
|
208
218
|
* modes (LOOP T-05): its bubbles and its one button in its own colour, and
|
|
209
219
|
* not in the theme's default — the chat sets its theme again inside it, so
|
|
210
|
-
* what the page around it set does not reach in.
|
|
220
|
+
* what the page around it set does not reach in. The accents are `loop`'s
|
|
221
|
+
* colours: over another theme (T-30) nothing is laid, the theme's own rule.
|
|
211
222
|
*/
|
|
212
|
-
export declare function appThemeOverrides(app: AppSpec): ThemeOverrides | undefined;
|
|
213
|
-
export declare function AppRenderer({ app, plugins, instance, onPresence, onSaying, paused, sidebar, frame, page, pluginsOff, ...embed }: AppRendererProps): React.JSX.Element;
|
|
223
|
+
export declare function appThemeOverrides(app: AppSpec, variant: string): ThemeOverrides | undefined;
|
|
224
|
+
export declare function AppRenderer({ app, plugins, instance, onPresence, onSaying, paused, sidebar, computer, frame, page, pluginsOff, ...embed }: AppRendererProps): React.JSX.Element;
|
|
214
225
|
export default AppRenderer;
|
|
@@ -45,7 +45,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
45
45
|
* @module loop/apps/AppRenderer
|
|
46
46
|
*/
|
|
47
47
|
import { useMemo } from 'react';
|
|
48
|
-
import { loopAccentStyles } from '@datalayer/primer-addons';
|
|
48
|
+
import { loopAccentStyles, useSystemColorMode, useThemeStore, } from '@datalayer/primer-addons';
|
|
49
49
|
import { defineAgentCapacityPlugin } from '../plugins/agent-capacity';
|
|
50
50
|
import { APP_PAGE_SURFACE, defineAppHostPagePlugin, defineAppPagePlugin, hasAppPage, } from '../plugins/app-page';
|
|
51
51
|
import { CANVAS_BLOCK_PLUGINS, canvasBlocksPluginName, } from '../plugins/canvas-blocks';
|
|
@@ -54,6 +54,8 @@ import { dumpAppspec } from './appspec';
|
|
|
54
54
|
import { defineAppActivityPlugin } from '../plugins/app-activity';
|
|
55
55
|
import { defineAppComputerPlugin } from '../plugins/app-computer';
|
|
56
56
|
import { defineAppRulesPlugin } from '../plugins/app-rules';
|
|
57
|
+
import { defineAppComposerPlugin } from './AppComposer';
|
|
58
|
+
import { modeEffect } from './composer';
|
|
57
59
|
import { defineAppFeedbackPlugin } from './AppFeedback';
|
|
58
60
|
import { defineAppKeptPlugin } from './AppKept';
|
|
59
61
|
import { keepsFeedback } from './feedback';
|
|
@@ -105,6 +107,14 @@ export function defineAppPlugin(app) {
|
|
|
105
107
|
// application's model when it names one (`loop/apps/agent`).
|
|
106
108
|
instructions: app.instructions,
|
|
107
109
|
model: app.model,
|
|
110
|
+
// Its modes (LOOP P-19), as the session API applies them: the
|
|
111
|
+
// instructions of the options chosen and the model one names, for the
|
|
112
|
+
// run they are sent with.
|
|
113
|
+
...((app.interface.modes ?? []).length > 0
|
|
114
|
+
? {
|
|
115
|
+
modeEffect: (chosen) => modeEffect(app, chosen),
|
|
116
|
+
}
|
|
117
|
+
: {}),
|
|
108
118
|
});
|
|
109
119
|
}
|
|
110
120
|
/**
|
|
@@ -146,11 +156,13 @@ export function appLayoutOptions(app) {
|
|
|
146
156
|
* its page (`app-page`) when it has one, with the Canvas's block plugins
|
|
147
157
|
* but those of the UI plugins its organization turned off (`pluginsOff`),
|
|
148
158
|
* whose contributions are the blocks it may draw (R-01b); a thumb and a
|
|
149
|
-
* comment on each answer when its record keeps feedback (V-18);
|
|
159
|
+
* comment on each answer when its record keeps feedback (V-18); its
|
|
160
|
+
* commands in the composer's `/` menu and its modes beside it (P-19); what it
|
|
150
161
|
* keeps and for how long, under its prompt before the first message, when
|
|
151
162
|
* `kept` says it (R-31); with
|
|
152
163
|
* `sidebar`, its rules and approvals card, its activity feed and its
|
|
153
|
-
* computer (R-01b, R-23);
|
|
164
|
+
* computer (R-01b, R-23); with `computer`, its computer alone — at its
|
|
165
|
+
* address, for its owner and the editors of its application;
|
|
154
166
|
* laid out as `interface.layout` says;
|
|
155
167
|
* - a **decision**: the page its host draws (`page`), as the one view of a
|
|
156
168
|
* workspace without a conversation (R-02).
|
|
@@ -180,6 +192,12 @@ export function appPreset(app, options = {}) {
|
|
|
180
192
|
plugins: [
|
|
181
193
|
defineAppPlugin(app),
|
|
182
194
|
...(withPage ? [defineAppPagePlugin(app), ...blocks] : []),
|
|
195
|
+
// Its commands in the composer's `/` menu and its modes beside the
|
|
196
|
+
// composer (LOOP P-19): only for an application that has some.
|
|
197
|
+
...((app.interface.commands ?? []).length > 0 ||
|
|
198
|
+
(app.interface.modes ?? []).length > 0
|
|
199
|
+
? [defineAppComposerPlugin(app)]
|
|
200
|
+
: []),
|
|
183
201
|
// A thumb and a comment on each answer, kept in its record (LOOP
|
|
184
202
|
// V-18): only for an application whose record keeps feedback.
|
|
185
203
|
...(keepsFeedback(app) ? [defineAppFeedbackPlugin(app)] : []),
|
|
@@ -196,7 +214,9 @@ export function appPreset(app, options = {}) {
|
|
|
196
214
|
// Its computer, live (R-23).
|
|
197
215
|
defineAppComputerPlugin(app),
|
|
198
216
|
]
|
|
199
|
-
:
|
|
217
|
+
: options.computer
|
|
218
|
+
? [defineAppComputerPlugin(app)]
|
|
219
|
+
: []),
|
|
200
220
|
],
|
|
201
221
|
workspace: appLayoutOptions(app),
|
|
202
222
|
};
|
|
@@ -249,18 +269,19 @@ const NO_PLUGINS = [];
|
|
|
249
269
|
* The application's accent over the theme its conversation wears, in both
|
|
250
270
|
* modes (LOOP T-05): its bubbles and its one button in its own colour, and
|
|
251
271
|
* not in the theme's default — the chat sets its theme again inside it, so
|
|
252
|
-
* what the page around it set does not reach in.
|
|
272
|
+
* what the page around it set does not reach in. The accents are `loop`'s
|
|
273
|
+
* colours: over another theme (T-30) nothing is laid, the theme's own rule.
|
|
253
274
|
*/
|
|
254
|
-
export function appThemeOverrides(app) {
|
|
275
|
+
export function appThemeOverrides(app, variant) {
|
|
255
276
|
const accent = app.interface?.accent;
|
|
256
|
-
return accent
|
|
277
|
+
return accent && variant === 'loop'
|
|
257
278
|
? {
|
|
258
279
|
light: loopAccentStyles(accent, 'light'),
|
|
259
280
|
dark: loopAccentStyles(accent, 'dark'),
|
|
260
281
|
}
|
|
261
282
|
: undefined;
|
|
262
283
|
}
|
|
263
|
-
export function AppRenderer({ app, plugins = NO_PLUGINS, instance, onPresence, onSaying, paused = false, sidebar = false, frame = false, page, pluginsOff, ...embed }) {
|
|
284
|
+
export function AppRenderer({ app, plugins = NO_PLUGINS, instance, onPresence, onSaying, paused = false, sidebar = false, computer = false, frame = false, page, pluginsOff, ...embed }) {
|
|
264
285
|
/*
|
|
265
286
|
* The kind's plugins, made once per application. `LoopEmbed` rebuilds its
|
|
266
287
|
* whole reactor when its plugins change, so a new plugin on every render
|
|
@@ -282,6 +303,7 @@ export function AppRenderer({ app, plugins = NO_PLUGINS, instance, onPresence, o
|
|
|
282
303
|
page,
|
|
283
304
|
pluginsOff,
|
|
284
305
|
sidebar,
|
|
306
|
+
computer,
|
|
285
307
|
appUid: instance?.appUid,
|
|
286
308
|
...(deployed ? { kept: { visitor } } : {}),
|
|
287
309
|
});
|
|
@@ -293,7 +315,7 @@ export function AppRenderer({ app, plugins = NO_PLUGINS, instance, onPresence, o
|
|
|
293
315
|
}
|
|
294
316
|
},
|
|
295
317
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
296
|
-
[source, page, off, sidebar, instance?.appUid, deployed, visitor]);
|
|
318
|
+
[source, page, off, sidebar, computer, instance?.appUid, deployed, visitor]);
|
|
297
319
|
const allPlugins = useMemo(() => ('problem' in preset ? plugins : [...preset.plugins, ...plugins]), [preset, plugins]);
|
|
298
320
|
/*
|
|
299
321
|
* On Datalayer, the application runs on a runtime: allocated with a plain
|
|
@@ -311,10 +333,24 @@ export function AppRenderer({ app, plugins = NO_PLUGINS, instance, onPresence, o
|
|
|
311
333
|
instance?.deploymentUid,
|
|
312
334
|
instance?.version,
|
|
313
335
|
]);
|
|
336
|
+
/*
|
|
337
|
+
* The theme it runs in (T-30): the one its host says, else the one its
|
|
338
|
+
* Appspec names, else the page's — the person's own. Its mode likewise;
|
|
339
|
+
* `auto` is the device's.
|
|
340
|
+
*/
|
|
341
|
+
const pageTheme = useThemeStore(state => state.theme);
|
|
342
|
+
const system = useSystemColorMode();
|
|
343
|
+
const named = app.interface?.theme;
|
|
344
|
+
const worn = embed.themeVariant ?? named?.variant ?? pageTheme;
|
|
345
|
+
const namedMode = named?.mode === 'auto'
|
|
346
|
+
? system
|
|
347
|
+
: named?.mode === 'light' || named?.mode === 'dark'
|
|
348
|
+
? named.mode
|
|
349
|
+
: undefined;
|
|
314
350
|
const accent = app.interface?.accent;
|
|
315
|
-
const themeOverrides = useMemo(() => appThemeOverrides(app),
|
|
351
|
+
const themeOverrides = useMemo(() => appThemeOverrides(app, worn),
|
|
316
352
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
317
|
-
[accent]);
|
|
353
|
+
[accent, worn]);
|
|
318
354
|
if ('problem' in preset) {
|
|
319
355
|
return (_jsx("div", { role: "status", style: { padding: 16 }, children: preset.problem }));
|
|
320
356
|
}
|
|
@@ -330,13 +366,10 @@ export function AppRenderer({ app, plugins = NO_PLUGINS, instance, onPresence, o
|
|
|
330
366
|
}
|
|
331
367
|
: {}),
|
|
332
368
|
// Its rules, activity and computer on a rail, one at a time (T-07).
|
|
333
|
-
sidebarRail: sidebar, teamPicker: false, showAgentVariants: false, graph: false, pluginsPanel: false, datalayerAgentSpecId: DATALAYER_BOOTSTRAP_AGENTSPEC,
|
|
369
|
+
sidebarRail: sidebar || computer, teamPicker: false, showAgentVariants: false, graph: false, pluginsPanel: false, datalayerAgentSpecId: DATALAYER_BOOTSTRAP_AGENTSPEC,
|
|
334
370
|
// Where an application runs is decided by its host — the Studio's
|
|
335
371
|
// Preview, the hosted page — not offered to its user.
|
|
336
|
-
targetFixed: true,
|
|
337
|
-
// Applications first (LOOP T-12): an application's conversation wears
|
|
338
|
-
// the `loop` theme; a host may say otherwise.
|
|
339
|
-
themeVariant: "loop", themeOverrides: themeOverrides,
|
|
372
|
+
targetFixed: true, ...(named ? { themeVariant: named.variant } : {}), ...(namedMode ? { colorMode: namedMode } : {}), themeOverrides: themeOverrides,
|
|
340
373
|
// Its own face, name and welcome in the chat (T-08); no counters: a
|
|
341
374
|
// person using an application is not asking about tokens.
|
|
342
375
|
presence: {
|
|
@@ -24,13 +24,20 @@
|
|
|
24
24
|
*
|
|
25
25
|
* @module loop/apps/appspec
|
|
26
26
|
*/
|
|
27
|
-
import type { AppAccent, AppAssistantCharacter, AppEmbedMode, AppVoiceSpec, AppKind, AppLayout, AppSpec } from '../../types/agentspecs';
|
|
27
|
+
import type { AppAccent, AppThemeMode, AppThemeVariant, AppAssistantCharacter, AppEmbedMode, AppVoiceSpec, AppKind, AppLayout, AppSpec } from '../../types/agentspecs';
|
|
28
28
|
/** The version of the spec itself. */
|
|
29
29
|
export declare const APP_SCHEMA = "loop.app/v1";
|
|
30
30
|
/** The face of an application that has not chosen one. */
|
|
31
31
|
export declare const DEFAULT_EMOJI = "\uD83D\uDC40";
|
|
32
32
|
export declare const APP_KINDS: AppKind[];
|
|
33
33
|
export declare const APP_ACCENTS: AppAccent[];
|
|
34
|
+
/**
|
|
35
|
+
* The themes an application may run in (LOOP T-30): Appearance's, as
|
|
36
|
+
* primer-addons lists them (`themeVariants`), in its order.
|
|
37
|
+
*/
|
|
38
|
+
export declare const APP_THEME_VARIANTS: AppThemeVariant[];
|
|
39
|
+
/** The colour modes a theme is worn in. */
|
|
40
|
+
export declare const APP_THEME_MODES: AppThemeMode[];
|
|
34
41
|
/** The four ways an application sits in another product's page (LOOP D-07). */
|
|
35
42
|
export declare const APP_EMBED_MODES: AppEmbedMode[];
|
|
36
43
|
/**
|
package/lib/loop/apps/appspec.js
CHANGED
|
@@ -16,6 +16,23 @@ export const APP_ACCENTS = [
|
|
|
16
16
|
'sun',
|
|
17
17
|
'violet',
|
|
18
18
|
];
|
|
19
|
+
/**
|
|
20
|
+
* The themes an application may run in (LOOP T-30): Appearance's, as
|
|
21
|
+
* primer-addons lists them (`themeVariants`), in its order.
|
|
22
|
+
*/
|
|
23
|
+
export const APP_THEME_VARIANTS = [
|
|
24
|
+
'datalayer',
|
|
25
|
+
'spatial',
|
|
26
|
+
'lovely',
|
|
27
|
+
'matrix',
|
|
28
|
+
'earth',
|
|
29
|
+
'sand',
|
|
30
|
+
'ivory',
|
|
31
|
+
'sun',
|
|
32
|
+
'loop',
|
|
33
|
+
];
|
|
34
|
+
/** The colour modes a theme is worn in. */
|
|
35
|
+
export const APP_THEME_MODES = ['light', 'dark', 'auto'];
|
|
19
36
|
/** The four ways an application sits in another product's page (LOOP D-07). */
|
|
20
37
|
export const APP_EMBED_MODES = [
|
|
21
38
|
'inline',
|
|
@@ -105,7 +122,8 @@ export function emptyAppspec(kind = 'chat') {
|
|
|
105
122
|
accent: 'green',
|
|
106
123
|
welcome: '',
|
|
107
124
|
starters: [],
|
|
108
|
-
|
|
125
|
+
commands: [],
|
|
126
|
+
modes: [],
|
|
109
127
|
components: [],
|
|
110
128
|
outputs: [],
|
|
111
129
|
},
|
|
@@ -129,6 +147,7 @@ export function emptyAppspec(kind = 'chat') {
|
|
|
129
147
|
notifications: [],
|
|
130
148
|
setup: [],
|
|
131
149
|
enabled: true,
|
|
150
|
+
unavailable_because: '',
|
|
132
151
|
tags: [],
|
|
133
152
|
icon: DEFAULT_ICON,
|
|
134
153
|
emoji: DEFAULT_EMOJI,
|
|
@@ -167,6 +186,7 @@ const KNOWN_KEYS = [
|
|
|
167
186
|
'notifications',
|
|
168
187
|
'decision',
|
|
169
188
|
'enabled',
|
|
189
|
+
'unavailable_because',
|
|
170
190
|
'tags',
|
|
171
191
|
'icon',
|
|
172
192
|
'emoji',
|
|
@@ -190,23 +210,38 @@ function parseRule(data) {
|
|
|
190
210
|
behaviour: oneOf(data.behaviour, BEHAVIOURS, 'ask_first'),
|
|
191
211
|
};
|
|
192
212
|
}
|
|
193
|
-
|
|
194
|
-
|
|
213
|
+
/** A slash command (LOOP P-19); a wrong one is said by the checks. */
|
|
214
|
+
function parseCommand(data) {
|
|
215
|
+
return {
|
|
216
|
+
name: text(data.name),
|
|
217
|
+
description: text(data.description),
|
|
218
|
+
prompt: text(data.prompt),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/** One position of a mode switch, what it does said only when said. */
|
|
222
|
+
function parseModeOption(data) {
|
|
223
|
+
const option = {
|
|
195
224
|
id: text(data.id),
|
|
196
|
-
type: oneOf(data.type, ['select', 'text', 'toggle', 'slider', 'number'], 'text'),
|
|
197
225
|
label: text(data.label),
|
|
198
|
-
options: texts(data.options),
|
|
199
226
|
};
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
setting.min = data.min;
|
|
227
|
+
for (const key of ['description', 'instructions', 'model']) {
|
|
228
|
+
if (typeof data[key] === 'string' && data[key]) {
|
|
229
|
+
option[key] = data[key];
|
|
230
|
+
}
|
|
205
231
|
}
|
|
206
|
-
|
|
207
|
-
|
|
232
|
+
return option;
|
|
233
|
+
}
|
|
234
|
+
/** A mode switch (LOOP P-19). */
|
|
235
|
+
function parseMode(data) {
|
|
236
|
+
const mode = {
|
|
237
|
+
id: text(data.id),
|
|
238
|
+
label: text(data.label),
|
|
239
|
+
options: records(data.options).map(parseModeOption),
|
|
240
|
+
};
|
|
241
|
+
if (typeof data.default === 'string' && data.default) {
|
|
242
|
+
mode.default = data.default;
|
|
208
243
|
}
|
|
209
|
-
return
|
|
244
|
+
return mode;
|
|
210
245
|
}
|
|
211
246
|
function parseSurface(data) {
|
|
212
247
|
return {
|
|
@@ -246,11 +281,18 @@ function parseInterface(data, kind) {
|
|
|
246
281
|
label: text(starter.label),
|
|
247
282
|
message: text(starter.message),
|
|
248
283
|
})),
|
|
249
|
-
|
|
284
|
+
commands: records(data.commands).map(parseCommand),
|
|
285
|
+
modes: records(data.modes).map(parseMode),
|
|
250
286
|
components: texts(data.components),
|
|
251
287
|
voice: parseVoice(data.voice),
|
|
252
288
|
outputs: texts(data.outputs),
|
|
253
289
|
};
|
|
290
|
+
// Its settings are the JSON Schema of a form (C-16), kept as written: what
|
|
291
|
+
// is not one — the list of settings of earlier versions among it — is not
|
|
292
|
+
// read, and is said by the checks.
|
|
293
|
+
if (isData(data.settings)) {
|
|
294
|
+
parsed.settings = JSON.parse(JSON.stringify(data.settings));
|
|
295
|
+
}
|
|
254
296
|
if (isData(data.surface)) {
|
|
255
297
|
parsed.surface = parseSurface(data.surface);
|
|
256
298
|
}
|
|
@@ -260,6 +302,14 @@ function parseInterface(data, kind) {
|
|
|
260
302
|
if (data.balloon === 'history' || data.balloon === 'current') {
|
|
261
303
|
parsed.balloon = data.balloon;
|
|
262
304
|
}
|
|
305
|
+
// A theme of Appearance's, or none: an unknown one is said by the checks.
|
|
306
|
+
if (isData(data.theme) &&
|
|
307
|
+
APP_THEME_VARIANTS.includes(data.theme.variant)) {
|
|
308
|
+
parsed.theme = { variant: data.theme.variant };
|
|
309
|
+
if (APP_THEME_MODES.includes(data.theme.mode)) {
|
|
310
|
+
parsed.theme.mode = data.theme.mode;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
263
313
|
return parsed;
|
|
264
314
|
}
|
|
265
315
|
function parseCriterion(data) {
|
|
@@ -433,6 +483,7 @@ export function parseAppspec(document) {
|
|
|
433
483
|
memory: text(data.memory),
|
|
434
484
|
notifications: texts(data.notifications),
|
|
435
485
|
enabled: flag(data.enabled, true),
|
|
486
|
+
unavailable_because: text(data.unavailable_because),
|
|
436
487
|
tags: texts(data.tags),
|
|
437
488
|
icon: text(data.icon, DEFAULT_ICON),
|
|
438
489
|
emoji: text(data.emoji, DEFAULT_EMOJI) || DEFAULT_EMOJI,
|
|
@@ -516,19 +567,27 @@ function dumpInterface(spec, kind) {
|
|
|
516
567
|
label: starter.label,
|
|
517
568
|
message: starter.message,
|
|
518
569
|
})))
|
|
519
|
-
.list('
|
|
570
|
+
.list('commands', spec.commands.map(command => ({
|
|
571
|
+
name: command.name,
|
|
572
|
+
description: command.description,
|
|
573
|
+
prompt: command.prompt,
|
|
574
|
+
})))
|
|
575
|
+
.list('modes', spec.modes.map(mode => {
|
|
520
576
|
const written = new Writer()
|
|
521
|
-
.text('id',
|
|
522
|
-
.text('
|
|
523
|
-
.
|
|
524
|
-
.
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
577
|
+
.text('id', mode.id, '\u0000')
|
|
578
|
+
.text('label', mode.label, '\u0000')
|
|
579
|
+
.list('options', mode.options.map(option => new Writer()
|
|
580
|
+
.text('id', option.id, '\u0000')
|
|
581
|
+
.text('label', option.label, '\u0000')
|
|
582
|
+
.text('description', option.description ?? '')
|
|
583
|
+
.text('instructions', option.instructions ?? '')
|
|
584
|
+
.text('model', option.model ?? '').data));
|
|
585
|
+
if (mode.default) {
|
|
586
|
+
written.data.default = mode.default;
|
|
529
587
|
}
|
|
530
588
|
return written.data;
|
|
531
589
|
}))
|
|
590
|
+
.part('settings', spec.settings ? { ...spec.settings } : {})
|
|
532
591
|
.list('components', spec.components);
|
|
533
592
|
if (spec.surface) {
|
|
534
593
|
writer.data.surface = dumpSurface(spec.surface);
|
|
@@ -539,6 +598,11 @@ function dumpInterface(spec, kind) {
|
|
|
539
598
|
if (spec.balloon) {
|
|
540
599
|
writer.data.balloon = spec.balloon;
|
|
541
600
|
}
|
|
601
|
+
if (spec.theme) {
|
|
602
|
+
writer.data.theme = spec.theme.mode
|
|
603
|
+
? { variant: spec.theme.variant, mode: spec.theme.mode }
|
|
604
|
+
: { variant: spec.theme.variant };
|
|
605
|
+
}
|
|
542
606
|
// Its voice, when it says one (VO-41): a spec made before voice has none.
|
|
543
607
|
if (spec.voice) {
|
|
544
608
|
writer.part('voice', new Writer()
|
|
@@ -678,6 +742,7 @@ export function dumpAppspec(app) {
|
|
|
678
742
|
}
|
|
679
743
|
writer
|
|
680
744
|
.value('enabled', app.enabled, true)
|
|
745
|
+
.text('unavailable_because', app.unavailable_because)
|
|
681
746
|
.list('tags', app.tags)
|
|
682
747
|
.text('icon', app.icon ?? DEFAULT_ICON, DEFAULT_ICON)
|
|
683
748
|
.text('emoji', app.emoji, DEFAULT_EMOJI)
|