@datalayer/agent-runtimes 1.3.82 → 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/components/a2ui/datalayer/Form.js +19 -2
- package/lib/loop/apps/appspec.js +11 -33
- package/lib/loop/apps/checks.js +18 -8
- package/lib/loop/plugins/app-page/appPageModel.d.ts +14 -5
- package/lib/loop/plugins/app-page/appPageModel.js +80 -88
- package/lib/specs/agents/agents.js +6 -3
- package/lib/specs/apps.js +188 -176
- package/lib/specs/appspecSchema.js +17 -87
- package/lib/specs/cogs.js +3 -2
- package/lib/types/agentspecs.d.ts +37 -10
- package/package.json +1 -1
- package/scripts/codegen/generate_apps.py +0 -1
|
@@ -12,6 +12,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
12
12
|
* its action is dispatched, so the action's context reads them. A form the
|
|
13
13
|
* schema refuses says why beside each field and sends nothing.
|
|
14
14
|
*
|
|
15
|
+
* A form without an action is a set of settings (C-16) — an application's
|
|
16
|
+
* `interface.settings` drawn beside its conversation: no button, its values
|
|
17
|
+
* written where `values` points as they are filled, for the next run to take;
|
|
18
|
+
* the runtime checks them against the same schema.
|
|
19
|
+
*
|
|
15
20
|
* @module components/a2ui/datalayer/Form
|
|
16
21
|
*/
|
|
17
22
|
import { useEffect, useMemo, useState } from 'react';
|
|
@@ -34,11 +39,23 @@ export function FormView({ props }) {
|
|
|
34
39
|
useEffect(() => {
|
|
35
40
|
setLocal(JSON.parse(shownKey));
|
|
36
41
|
}, [shownKey]);
|
|
37
|
-
|
|
42
|
+
// Without an action, nothing is sent: no button, the values written as filled.
|
|
43
|
+
const live = action === undefined;
|
|
44
|
+
const uiSchema = useMemo(() => ({
|
|
45
|
+
'ui:submitButtonOptions': live
|
|
46
|
+
? { norender: true }
|
|
47
|
+
: { submitText: submitLabel },
|
|
48
|
+
}), [live, submitLabel]);
|
|
38
49
|
const problem = schema.type !== undefined && schema.type !== 'object'
|
|
39
50
|
? 'Its fields are not an object schema: a form asks for several named values.'
|
|
40
51
|
: null;
|
|
41
|
-
return (_jsx(BlockFrame, { title: title, label: "Form", weight: props.weight, testId: "a2ui-form", children: problem ? (_jsx(Problem, { children: problem })) : (_jsx(SchemaForm, { schema: schema, uiSchema: uiSchema, formData: values, validator: validator, liveValidate: touched, onBlur: () => setTouched(true), onError: () => setTouched(true), showErrorList: false, onChange: event =>
|
|
52
|
+
return (_jsx(BlockFrame, { title: title, label: "Form", weight: props.weight, testId: "a2ui-form", children: problem ? (_jsx(Problem, { children: problem })) : (_jsx(SchemaForm, { schema: schema, uiSchema: uiSchema, formData: values, validator: validator, liveValidate: touched, onBlur: () => setTouched(true), onError: () => setTouched(true), showErrorList: false, onChange: event => {
|
|
53
|
+
const filled = (event.formData ?? {});
|
|
54
|
+
setLocal(filled);
|
|
55
|
+
if (live) {
|
|
56
|
+
setValues(filled);
|
|
57
|
+
}
|
|
58
|
+
}, onSubmit: event => {
|
|
42
59
|
setValues((event.formData ?? {}));
|
|
43
60
|
action?.();
|
|
44
61
|
} })) }));
|
package/lib/loop/apps/appspec.js
CHANGED
|
@@ -124,7 +124,6 @@ export function emptyAppspec(kind = 'chat') {
|
|
|
124
124
|
starters: [],
|
|
125
125
|
commands: [],
|
|
126
126
|
modes: [],
|
|
127
|
-
settings: [],
|
|
128
127
|
components: [],
|
|
129
128
|
outputs: [],
|
|
130
129
|
},
|
|
@@ -148,6 +147,7 @@ export function emptyAppspec(kind = 'chat') {
|
|
|
148
147
|
notifications: [],
|
|
149
148
|
setup: [],
|
|
150
149
|
enabled: true,
|
|
150
|
+
unavailable_because: '',
|
|
151
151
|
tags: [],
|
|
152
152
|
icon: DEFAULT_ICON,
|
|
153
153
|
emoji: DEFAULT_EMOJI,
|
|
@@ -186,6 +186,7 @@ const KNOWN_KEYS = [
|
|
|
186
186
|
'notifications',
|
|
187
187
|
'decision',
|
|
188
188
|
'enabled',
|
|
189
|
+
'unavailable_because',
|
|
189
190
|
'tags',
|
|
190
191
|
'icon',
|
|
191
192
|
'emoji',
|
|
@@ -242,24 +243,6 @@ function parseMode(data) {
|
|
|
242
243
|
}
|
|
243
244
|
return mode;
|
|
244
245
|
}
|
|
245
|
-
function parseSetting(data) {
|
|
246
|
-
const setting = {
|
|
247
|
-
id: text(data.id),
|
|
248
|
-
type: oneOf(data.type, ['select', 'text', 'toggle', 'slider', 'number'], 'text'),
|
|
249
|
-
label: text(data.label),
|
|
250
|
-
options: texts(data.options),
|
|
251
|
-
};
|
|
252
|
-
if (['string', 'boolean', 'number'].includes(typeof data.default)) {
|
|
253
|
-
setting.default = data.default;
|
|
254
|
-
}
|
|
255
|
-
if (typeof data.min === 'number') {
|
|
256
|
-
setting.min = data.min;
|
|
257
|
-
}
|
|
258
|
-
if (typeof data.max === 'number') {
|
|
259
|
-
setting.max = data.max;
|
|
260
|
-
}
|
|
261
|
-
return setting;
|
|
262
|
-
}
|
|
263
246
|
function parseSurface(data) {
|
|
264
247
|
return {
|
|
265
248
|
protocol: text(data.protocol, DEFAULT_PROTOCOL),
|
|
@@ -300,11 +283,16 @@ function parseInterface(data, kind) {
|
|
|
300
283
|
})),
|
|
301
284
|
commands: records(data.commands).map(parseCommand),
|
|
302
285
|
modes: records(data.modes).map(parseMode),
|
|
303
|
-
settings: records(data.settings).map(parseSetting),
|
|
304
286
|
components: texts(data.components),
|
|
305
287
|
voice: parseVoice(data.voice),
|
|
306
288
|
outputs: texts(data.outputs),
|
|
307
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
|
+
}
|
|
308
296
|
if (isData(data.surface)) {
|
|
309
297
|
parsed.surface = parseSurface(data.surface);
|
|
310
298
|
}
|
|
@@ -495,6 +483,7 @@ export function parseAppspec(document) {
|
|
|
495
483
|
memory: text(data.memory),
|
|
496
484
|
notifications: texts(data.notifications),
|
|
497
485
|
enabled: flag(data.enabled, true),
|
|
486
|
+
unavailable_because: text(data.unavailable_because),
|
|
498
487
|
tags: texts(data.tags),
|
|
499
488
|
icon: text(data.icon, DEFAULT_ICON),
|
|
500
489
|
emoji: text(data.emoji, DEFAULT_EMOJI) || DEFAULT_EMOJI,
|
|
@@ -598,19 +587,7 @@ function dumpInterface(spec, kind) {
|
|
|
598
587
|
}
|
|
599
588
|
return written.data;
|
|
600
589
|
}))
|
|
601
|
-
.
|
|
602
|
-
const written = new Writer()
|
|
603
|
-
.text('id', setting.id, '\u0000')
|
|
604
|
-
.text('type', setting.type, '\u0000')
|
|
605
|
-
.text('label', setting.label, '\u0000')
|
|
606
|
-
.list('options', setting.options);
|
|
607
|
-
for (const key of ['default', 'min', 'max']) {
|
|
608
|
-
if (setting[key] !== undefined) {
|
|
609
|
-
written.data[key] = setting[key];
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
return written.data;
|
|
613
|
-
}))
|
|
590
|
+
.part('settings', spec.settings ? { ...spec.settings } : {})
|
|
614
591
|
.list('components', spec.components);
|
|
615
592
|
if (spec.surface) {
|
|
616
593
|
writer.data.surface = dumpSurface(spec.surface);
|
|
@@ -765,6 +742,7 @@ export function dumpAppspec(app) {
|
|
|
765
742
|
}
|
|
766
743
|
writer
|
|
767
744
|
.value('enabled', app.enabled, true)
|
|
745
|
+
.text('unavailable_because', app.unavailable_because)
|
|
768
746
|
.list('tags', app.tags)
|
|
769
747
|
.text('icon', app.icon ?? DEFAULT_ICON, DEFAULT_ICON)
|
|
770
748
|
.text('emoji', app.emoji, DEFAULT_EMOJI)
|
package/lib/loop/apps/checks.js
CHANGED
|
@@ -95,6 +95,14 @@ function shapeProblems(app) {
|
|
|
95
95
|
if (Boolean(app.agent) === Boolean(app.team)) {
|
|
96
96
|
problems.push('An application names who does the work: an `agent`, or a `team`, and not both.');
|
|
97
97
|
}
|
|
98
|
+
// agentspecs refuses either way round (LOOP E-01): off says why, on says nothing.
|
|
99
|
+
const because = (app.unavailable_because ?? '').trim();
|
|
100
|
+
if (!app.enabled && !because) {
|
|
101
|
+
problems.push('An application that is not offered says why, under `unavailable_because`.');
|
|
102
|
+
}
|
|
103
|
+
if (app.enabled && because) {
|
|
104
|
+
problems.push('An application offered today is available: remove `unavailable_because`, or set `enabled: false`.');
|
|
105
|
+
}
|
|
98
106
|
if (app.kind === 'decision' && !app.decision) {
|
|
99
107
|
problems.push('A decision application says what it decides, under `decision`.');
|
|
100
108
|
}
|
|
@@ -472,7 +480,6 @@ const ENUMS = {
|
|
|
472
480
|
behaviour: ['do_it', 'if_asked', 'ask_first', 'leave_to_me'],
|
|
473
481
|
layout: ['chat', 'page', 'split'],
|
|
474
482
|
accent: ['green', 'rose', 'sky', 'lime', 'sun', 'violet'],
|
|
475
|
-
settingType: ['select', 'text', 'toggle', 'slider', 'number'],
|
|
476
483
|
record: [
|
|
477
484
|
'conversations',
|
|
478
485
|
'actions',
|
|
@@ -721,13 +728,16 @@ export function documentShapeProblems(document) {
|
|
|
721
728
|
if (choosingModel.length > 1) {
|
|
722
729
|
at('interface.modes', `let only one mode choose the model, not ${choosingModel.join(', ')}`);
|
|
723
730
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
+
// Its settings are the JSON Schema of a form (C-16), checked as a Form's
|
|
732
|
+
// schema is; the list of settings of earlier versions is refused.
|
|
733
|
+
if (Array.isArray(ui.settings)) {
|
|
734
|
+
at('interface.settings', 'is the JSON Schema of a form, an object of named fields (LOOP C-16), not a list of settings: say each one as a property, its title and its default');
|
|
735
|
+
}
|
|
736
|
+
else {
|
|
737
|
+
mapping(ui.settings, 'interface.settings', settings => {
|
|
738
|
+
problems.push(...formProblems({ id: 'settings', schema: settings }));
|
|
739
|
+
});
|
|
740
|
+
}
|
|
731
741
|
mapping(ui.surface, 'interface.surface', surface => {
|
|
732
742
|
records(surface.components, 'interface.surface.components', (item, where) => {
|
|
733
743
|
required(item, 'id', where);
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*
|
|
24
24
|
* @module loop/plugins/app-page/appPageModel
|
|
25
25
|
*/
|
|
26
|
-
import type {
|
|
26
|
+
import type { AppFormField, AppFormSchema, AppSpec } from '../../../types/agentspecs';
|
|
27
27
|
import type { ChatTurnSnapshot, ConversationEntry } from '../../core';
|
|
28
28
|
/** The id of the editor surface an application's page is drawn in. */
|
|
29
29
|
export declare const APP_PAGE_SURFACE = "app-page";
|
|
@@ -77,6 +77,15 @@ export declare const APP_KIND_PATHS: Record<AppPageKind, AppKindPaths>;
|
|
|
77
77
|
export declare const isAppPageKind: (kind: string) => kind is AppPageKind;
|
|
78
78
|
/** The path a setting, an input of the page, is kept at. */
|
|
79
79
|
export declare const inputPath: (id: string) => string;
|
|
80
|
+
/**
|
|
81
|
+
* The fields of an application's settings (C-16), by name, in the order its
|
|
82
|
+
* form says them; none when it has no settings.
|
|
83
|
+
*/
|
|
84
|
+
export declare function settingFields(settings: AppFormSchema | undefined): Array<[string, AppFormField]>;
|
|
85
|
+
/** A setting as a person reads it: its field's `title`, else its name. */
|
|
86
|
+
export declare const settingTitle: (id: string, field: AppFormField) => string;
|
|
87
|
+
/** What each setting starts at: its field's `default`, for those that say one. */
|
|
88
|
+
export declare function settingDefaults(settings: AppFormSchema | undefined): Record<string, unknown>;
|
|
80
89
|
/**
|
|
81
90
|
* What an application publishes and takes, with its own settings: the kind's
|
|
82
91
|
* list, and for a kind that has inputs each setting at `/inputs/<id>`, both
|
|
@@ -95,8 +104,6 @@ export declare function hasAppPage(app: Pick<AppSpec, 'kind' | 'interface' | 'ag
|
|
|
95
104
|
* none): the application's layout is `chat`, the conversation alone.
|
|
96
105
|
*/
|
|
97
106
|
export declare function surfaceUnshown(app: Pick<AppSpec, 'kind' | 'interface' | 'agent'>): string | null;
|
|
98
|
-
/** A setting's first value, as its input holds it. */
|
|
99
|
-
export declare function initialInput(setting: AppSettingSpec): unknown;
|
|
100
107
|
/**
|
|
101
108
|
* What the conversation publishes to the page, by path, now: from its
|
|
102
109
|
* current turn, and for a chat its whole conversation.
|
|
@@ -136,8 +143,10 @@ export declare function givenFiles(files: unknown): {
|
|
|
136
143
|
};
|
|
137
144
|
/**
|
|
138
145
|
* The application's settings as the page holds them, as the session API
|
|
139
|
-
* takes them
|
|
140
|
-
* as on or off
|
|
146
|
+
* takes them, each as its field's type says (C-16): a number as a number, a
|
|
147
|
+
* boolean as on or off, a word as the option chosen — a ChoicePicker holds a
|
|
148
|
+
* list of them — each setting the page has a value for. The runtime checks
|
|
149
|
+
* them against the settings' form.
|
|
141
150
|
*/
|
|
142
151
|
export declare function settingsOf(app: Pick<AppSpec, 'interface'>, inputs: unknown): Record<string, unknown>;
|
|
143
152
|
/**
|
|
@@ -141,6 +141,25 @@ export const APP_KIND_PATHS = {
|
|
|
141
141
|
export const isAppPageKind = (kind) => Object.prototype.hasOwnProperty.call(APP_KIND_PATHS, kind);
|
|
142
142
|
/** The path a setting, an input of the page, is kept at. */
|
|
143
143
|
export const inputPath = (id) => `/inputs/${id}`;
|
|
144
|
+
/**
|
|
145
|
+
* The fields of an application's settings (C-16), by name, in the order its
|
|
146
|
+
* form says them; none when it has no settings.
|
|
147
|
+
*/
|
|
148
|
+
export function settingFields(settings) {
|
|
149
|
+
const properties = settings?.properties;
|
|
150
|
+
if (!properties || typeof properties !== 'object') {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
return Object.entries(properties).filter((entry) => typeof entry[1] === 'object' && entry[1] !== null);
|
|
154
|
+
}
|
|
155
|
+
/** A setting as a person reads it: its field's `title`, else its name. */
|
|
156
|
+
export const settingTitle = (id, field) => typeof field.title === 'string' && field.title ? field.title : id;
|
|
157
|
+
/** What each setting starts at: its field's `default`, for those that say one. */
|
|
158
|
+
export function settingDefaults(settings) {
|
|
159
|
+
return Object.fromEntries(settingFields(settings)
|
|
160
|
+
.filter(([, field]) => field.default !== undefined)
|
|
161
|
+
.map(([id, field]) => [id, field.default]));
|
|
162
|
+
}
|
|
144
163
|
/**
|
|
145
164
|
* What an application publishes and takes, with its own settings: the kind's
|
|
146
165
|
* list, and for a kind that has inputs each setting at `/inputs/<id>`, both
|
|
@@ -154,10 +173,10 @@ export function appKindPaths(app) {
|
|
|
154
173
|
if (!kind.inputs) {
|
|
155
174
|
return kind;
|
|
156
175
|
}
|
|
157
|
-
const inputs = app.interface.settings.map(
|
|
158
|
-
path: inputPath(
|
|
159
|
-
meaning: `The setting “${
|
|
160
|
-
words:
|
|
176
|
+
const inputs = settingFields(app.interface.settings).map(([id, field]) => ({
|
|
177
|
+
path: inputPath(id),
|
|
178
|
+
meaning: `The setting “${settingTitle(id, field)}”.`,
|
|
179
|
+
words: settingTitle(id, field),
|
|
161
180
|
}));
|
|
162
181
|
return {
|
|
163
182
|
...kind,
|
|
@@ -199,24 +218,6 @@ const STATUS_WORDS = {
|
|
|
199
218
|
done: 'Answered',
|
|
200
219
|
error: 'Stopped',
|
|
201
220
|
};
|
|
202
|
-
/** A setting's first value, as its input holds it. */
|
|
203
|
-
export function initialInput(setting) {
|
|
204
|
-
switch (setting.type) {
|
|
205
|
-
case 'toggle':
|
|
206
|
-
return Boolean(setting.default ?? false);
|
|
207
|
-
case 'select':
|
|
208
|
-
// A ChoicePicker holds a list of the values chosen.
|
|
209
|
-
return setting.default === undefined ? [] : [String(setting.default)];
|
|
210
|
-
case 'slider':
|
|
211
|
-
return Number(setting.default ?? setting.min ?? 0);
|
|
212
|
-
case 'number':
|
|
213
|
-
return setting.max === undefined
|
|
214
|
-
? String(setting.default ?? '')
|
|
215
|
-
: Number(setting.default ?? setting.min ?? 0);
|
|
216
|
-
default:
|
|
217
|
-
return String(setting.default ?? '');
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
221
|
/**
|
|
221
222
|
* What the conversation publishes to the page, by path, now: from its
|
|
222
223
|
* current turn, and for a chat its whole conversation.
|
|
@@ -253,10 +254,20 @@ export function appPageInitialData(app) {
|
|
|
253
254
|
}
|
|
254
255
|
const paths = appKindPaths(app);
|
|
255
256
|
if (paths.inputs) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
257
|
+
// Each setting at its default, as the settings' form holds it; a
|
|
258
|
+
// ChoicePicker a builder placed at a setting holds a list of the values
|
|
259
|
+
// chosen.
|
|
260
|
+
const inputs = settingDefaults(app.interface.settings);
|
|
261
|
+
for (const node of app.interface.surface?.components ?? []) {
|
|
262
|
+
const bound = node.value?.path;
|
|
263
|
+
const id = typeof bound === 'string' && bound.startsWith('/inputs/')
|
|
264
|
+
? bound.slice('/inputs/'.length)
|
|
265
|
+
: undefined;
|
|
266
|
+
if (node.component === 'ChoicePicker' && id && id in inputs) {
|
|
267
|
+
inputs[id] = Array.isArray(inputs[id]) ? inputs[id] : [inputs[id]];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
tree.inputs = inputs;
|
|
260
271
|
}
|
|
261
272
|
if (paths.accepts.some(entry => entry.path === DRAFT.path)) {
|
|
262
273
|
tree.draft = '';
|
|
@@ -289,9 +300,9 @@ export function inputsInWords(app, inputs) {
|
|
|
289
300
|
: {};
|
|
290
301
|
const lines = [];
|
|
291
302
|
const seen = new Set();
|
|
292
|
-
for (const
|
|
293
|
-
seen.add(
|
|
294
|
-
lines.push(inputWords(
|
|
303
|
+
for (const [id, field] of settingFields(app.interface.settings)) {
|
|
304
|
+
seen.add(id);
|
|
305
|
+
lines.push(inputWords(settingTitle(id, field), values[id]));
|
|
295
306
|
}
|
|
296
307
|
for (const [id, value] of Object.entries(values)) {
|
|
297
308
|
if (!seen.has(id)) {
|
|
@@ -345,38 +356,43 @@ export function givenFiles(files) {
|
|
|
345
356
|
}
|
|
346
357
|
/**
|
|
347
358
|
* The application's settings as the page holds them, as the session API
|
|
348
|
-
* takes them
|
|
349
|
-
* as on or off
|
|
359
|
+
* takes them, each as its field's type says (C-16): a number as a number, a
|
|
360
|
+
* boolean as on or off, a word as the option chosen — a ChoicePicker holds a
|
|
361
|
+
* list of them — each setting the page has a value for. The runtime checks
|
|
362
|
+
* them against the settings' form.
|
|
350
363
|
*/
|
|
351
364
|
export function settingsOf(app, inputs) {
|
|
352
365
|
const values = inputs && typeof inputs === 'object'
|
|
353
366
|
? inputs
|
|
354
367
|
: {};
|
|
355
368
|
const settings = {};
|
|
356
|
-
for (const
|
|
357
|
-
const
|
|
369
|
+
for (const [id, field] of settingFields(app.interface.settings)) {
|
|
370
|
+
const held = values[id];
|
|
371
|
+
if (held === undefined || held === null) {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (field.type === 'array' || field.type === 'object') {
|
|
375
|
+
settings[id] = held;
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
const value = Array.isArray(held) ? held[0] : held;
|
|
358
379
|
if (value === undefined || value === null) {
|
|
359
380
|
continue;
|
|
360
381
|
}
|
|
361
|
-
switch (
|
|
362
|
-
case '
|
|
363
|
-
const chosen = Array.isArray(value) ? value[0] : value;
|
|
364
|
-
if (chosen !== undefined && chosen !== '') {
|
|
365
|
-
settings[setting.id] = String(chosen);
|
|
366
|
-
}
|
|
367
|
-
break;
|
|
368
|
-
}
|
|
369
|
-
case 'toggle':
|
|
370
|
-
settings[setting.id] = Boolean(value);
|
|
371
|
-
break;
|
|
372
|
-
case 'slider':
|
|
382
|
+
switch (field.type) {
|
|
383
|
+
case 'integer':
|
|
373
384
|
case 'number':
|
|
374
385
|
if (value !== '' && !Number.isNaN(Number(value))) {
|
|
375
|
-
settings[
|
|
386
|
+
settings[id] = Number(value);
|
|
376
387
|
}
|
|
377
388
|
break;
|
|
389
|
+
case 'boolean':
|
|
390
|
+
settings[id] = Boolean(value);
|
|
391
|
+
break;
|
|
378
392
|
default:
|
|
379
|
-
|
|
393
|
+
if (value !== '' || !Array.isArray(field.enum)) {
|
|
394
|
+
settings[id] = typeof value === 'string' ? value : String(value);
|
|
395
|
+
}
|
|
380
396
|
}
|
|
381
397
|
}
|
|
382
398
|
return settings;
|
|
@@ -464,44 +480,22 @@ const button = (id, label, name, primary = false) => [
|
|
|
464
480
|
},
|
|
465
481
|
{ id: `${id}-label`, component: 'Text', text: label },
|
|
466
482
|
];
|
|
467
|
-
/**
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
value: option,
|
|
484
|
-
})),
|
|
485
|
-
};
|
|
486
|
-
case 'slider':
|
|
487
|
-
return {
|
|
488
|
-
...base,
|
|
489
|
-
component: 'Slider',
|
|
490
|
-
min: setting.min ?? 0,
|
|
491
|
-
max: setting.max ?? 100,
|
|
492
|
-
};
|
|
493
|
-
case 'number':
|
|
494
|
-
return setting.max === undefined
|
|
495
|
-
? { ...base, component: 'TextField', variant: 'number' }
|
|
496
|
-
: {
|
|
497
|
-
...base,
|
|
498
|
-
component: 'Slider',
|
|
499
|
-
min: setting.min ?? 0,
|
|
500
|
-
max: setting.max,
|
|
501
|
-
};
|
|
502
|
-
default:
|
|
503
|
-
return { ...base, component: 'TextField' };
|
|
504
|
-
}
|
|
483
|
+
/**
|
|
484
|
+
* The settings as one Form block (C-16): their form drawn with
|
|
485
|
+
* `@datalayer/primer-rjsf`, the values written at `/inputs` as they are
|
|
486
|
+
* filled — no action, no button: they go with the next run.
|
|
487
|
+
*/
|
|
488
|
+
function settingsForm(app) {
|
|
489
|
+
return app.interface.settings && settingFields(app.interface.settings).length
|
|
490
|
+
? [
|
|
491
|
+
{
|
|
492
|
+
id: 'settings',
|
|
493
|
+
component: 'Form',
|
|
494
|
+
schema: app.interface.settings,
|
|
495
|
+
values: { path: '/inputs' },
|
|
496
|
+
},
|
|
497
|
+
]
|
|
498
|
+
: [];
|
|
505
499
|
}
|
|
506
500
|
/**
|
|
507
501
|
* The page drawn when the application has a page layout and no surface
|
|
@@ -509,9 +503,7 @@ function settingInput(setting) {
|
|
|
509
503
|
* buttons its kind handles that the conversation does not already give.
|
|
510
504
|
*/
|
|
511
505
|
export function defaultAppSurface(app) {
|
|
512
|
-
const inputs = appKindPaths(app).inputs
|
|
513
|
-
? app.interface.settings.map(settingInput)
|
|
514
|
-
: [];
|
|
506
|
+
const inputs = appKindPaths(app).inputs ? settingsForm(app) : [];
|
|
515
507
|
const body = [];
|
|
516
508
|
switch (app.kind) {
|
|
517
509
|
case 'chat':
|
|
@@ -4900,8 +4900,9 @@ export const JUPYTER_DATA_ANALYST_AGENTSPEC_0_0_1 = {
|
|
|
4900
4900
|
'collaboration',
|
|
4901
4901
|
],
|
|
4902
4902
|
domain: undefined,
|
|
4903
|
-
enabled:
|
|
4903
|
+
enabled: true,
|
|
4904
4904
|
model: 'bedrock:us.anthropic.claude-sonnet-4-6',
|
|
4905
|
+
modelAdditionals: ['alibaba:qwen-max'],
|
|
4905
4906
|
mcpServers: [],
|
|
4906
4907
|
skills: [].filter(Boolean),
|
|
4907
4908
|
backendTools: [TOOL_MAP['runtime-echo:0.0.1']],
|
|
@@ -7966,8 +7967,9 @@ export const WORKER_CUSTOMER_INTERVIEWER_AGENTSPEC_0_0_1 = {
|
|
|
7966
7967
|
description: `Conducts adaptive, AI-led interviews that respond intelligently to each answer. It asks relevant follow-up questions, uncovers motivations and decision-making patterns, and transforms conversations into structured, actionable insights.`,
|
|
7967
7968
|
tags: ['research', 'customer-support', 'analysis'],
|
|
7968
7969
|
domain: 'market-analyst',
|
|
7969
|
-
enabled:
|
|
7970
|
+
enabled: true,
|
|
7970
7971
|
model: 'bedrock:us.anthropic.claude-sonnet-4-6',
|
|
7972
|
+
modelAdditionals: ['alibaba:qwen-max'],
|
|
7971
7973
|
mcpServers: [],
|
|
7972
7974
|
skills: [
|
|
7973
7975
|
SKILL_MAP['text-summarizer:0.0.1']
|
|
@@ -8249,8 +8251,9 @@ export const WORKER_DOCUMENT_QA_AGENTSPEC_0_0_1 = {
|
|
|
8249
8251
|
'qa',
|
|
8250
8252
|
],
|
|
8251
8253
|
domain: 'personal-assistant',
|
|
8252
|
-
enabled:
|
|
8254
|
+
enabled: true,
|
|
8253
8255
|
model: 'bedrock:us.anthropic.claude-sonnet-4-6',
|
|
8256
|
+
modelAdditionals: ['alibaba:qwen-max'],
|
|
8254
8257
|
mcpServers: [],
|
|
8255
8258
|
skills: [
|
|
8256
8259
|
SKILL_MAP['events:0.0.1']
|