@datalayer/agent-runtimes 1.3.84 → 1.3.86
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/ChatFloating.d.ts +5 -4
- package/lib/chat/ChatFloating.js +8 -6
- package/lib/chat/ChatLanguage.d.ts +17 -0
- package/lib/chat/ChatLanguage.js +29 -0
- package/lib/chat/assistant/AssistantStage.d.ts +4 -4
- package/lib/chat/assistant/AssistantStage.js +4 -2
- package/lib/chat/assistant/BalloonParts.d.ts +2 -1
- package/lib/chat/assistant/BalloonParts.js +12 -5
- package/lib/chat/assistant/BalloonVisual.js +3 -1
- package/lib/chat/assistant/ConversationBalloon.d.ts +1 -1
- package/lib/chat/assistant/ConversationBalloon.js +3 -2
- package/lib/chat/assistant/SpeechBalloon.d.ts +1 -1
- package/lib/chat/assistant/SpeechBalloon.js +10 -7
- package/lib/chat/assistant/toolLine.d.ts +8 -4
- package/lib/chat/assistant/toolLine.js +13 -9
- package/lib/chat/base/ChatBase.js +14 -11
- package/lib/chat/display/EmptyState.js +3 -1
- package/lib/chat/index.d.ts +2 -0
- package/lib/chat/index.js +3 -0
- package/lib/chat/messages/ChatMessages.js +3 -1
- package/lib/chat/messages/TurnFooter.js +3 -1
- package/lib/chat/prompt/InputPrompt.js +3 -1
- package/lib/chat/prompt/InputPromptLexical.js +3 -1
- package/lib/chat/prompt/footer/InPromptFooter.js +3 -1
- package/lib/chat/tools/ToolApprovalBanner.js +5 -3
- package/lib/chat/tools/ToolApprovalDialog.js +5 -3
- package/lib/chat/tools/ToolCallDisplay.js +29 -25
- package/lib/chat/words.d.ts +102 -0
- package/lib/chat/words.js +460 -0
- package/lib/components/a2ui/custom/index.d.ts +39 -0
- package/lib/components/a2ui/custom/index.js +95 -0
- package/lib/components/a2ui/custom/sandbox.d.ts +73 -0
- package/lib/components/a2ui/custom/sandbox.js +188 -0
- package/lib/components/a2ui/datalayer/index.d.ts +1 -1
- package/lib/components/a2ui/datalayer/index.js +1 -1
- package/lib/components/a2ui/datalayer/schema.d.ts +10 -0
- package/lib/components/a2ui/datalayer/schema.js +12 -1
- package/lib/components/a2ui/index.d.ts +1 -0
- package/lib/components/a2ui/index.js +1 -0
- package/lib/components/a2ui/visibleWhen.d.ts +12 -1
- package/lib/components/a2ui/visibleWhen.js +24 -3
- package/lib/components/inspector/AgentInspector.d.ts +25 -44
- package/lib/components/inspector/AgentInspector.js +27 -215
- package/lib/components/inspector/AgentInspectorDialog.d.ts +6 -6
- package/lib/components/inspector/AgentInspectorDialog.js +2 -2
- package/lib/components/inspector/a2aSpans.d.ts +51 -0
- package/lib/components/inspector/a2aSpans.js +373 -0
- package/lib/components/inspector/agentSpans.d.ts +120 -0
- package/lib/components/inspector/agentSpans.js +253 -0
- package/lib/components/inspector/chatSpans.d.ts +36 -0
- package/lib/components/inspector/chatSpans.js +50 -0
- package/lib/components/inspector/classify.d.ts +11 -8
- package/lib/components/inspector/classify.js +23 -24
- package/lib/components/inspector/index.d.ts +7 -7
- package/lib/components/inspector/index.js +7 -7
- package/lib/components/teams/A2ATeamGraph.d.ts +117 -22
- package/lib/components/teams/A2ATeamGraph.js +217 -97
- package/lib/components/teams/SceneTranscript.d.ts +29 -0
- package/lib/components/teams/SceneTranscript.js +54 -0
- package/lib/components/teams/SceneView.d.ts +41 -0
- package/lib/components/teams/SceneView.js +28 -0
- package/lib/components/teams/index.d.ts +4 -0
- package/lib/components/teams/index.js +4 -0
- package/lib/components/teams/sceneStage.d.ts +120 -0
- package/lib/components/teams/sceneStage.js +170 -0
- package/lib/components/teams/sceneTranscript.d.ts +74 -0
- package/lib/components/teams/sceneTranscript.js +294 -0
- package/lib/components/teams/teamConnections.d.ts +2 -2
- package/lib/components/teams/teamConnections.js +3 -3
- package/lib/components/teams/useA2ATeam.d.ts +94 -22
- package/lib/components/teams/useA2ATeam.js +356 -140
- package/lib/examples/AgentA2ATeamExample.d.ts +10 -9
- package/lib/examples/AgentA2ATeamExample.js +70 -60
- package/lib/examples/AssistantExample.js +2 -2
- package/lib/examples/ScenesExample.d.ts +26 -0
- package/lib/examples/ScenesExample.js +241 -0
- package/lib/examples/example-selector.js +1 -0
- package/lib/examples/exampleGroups.js +3 -0
- package/lib/examples/index.d.ts +1 -0
- package/lib/examples/index.js +1 -0
- package/lib/examples/main.js +4 -0
- package/lib/loop/apps/AppComposer.d.ts +2 -1
- package/lib/loop/apps/AppComposer.js +6 -3
- package/lib/loop/apps/AppRenderer.d.ts +2 -1
- package/lib/loop/apps/AppRenderer.js +34 -25
- package/lib/loop/apps/appspec.d.ts +11 -1
- package/lib/loop/apps/appspec.js +115 -1
- package/lib/loop/apps/checks.d.ts +8 -0
- package/lib/loop/apps/checks.js +106 -4
- package/lib/loop/apps/customComponents.d.ts +41 -0
- package/lib/loop/apps/customComponents.js +260 -0
- package/lib/loop/apps/hostTools.d.ts +8 -1
- package/lib/loop/apps/hostTools.js +9 -0
- package/lib/loop/apps/index.d.ts +1 -0
- package/lib/loop/apps/index.js +1 -0
- package/lib/loop/core/a2uiComponents.d.ts +10 -0
- package/lib/loop/core/a2uiComponents.js +15 -0
- package/lib/loop/core/index.d.ts +1 -0
- package/lib/loop/core/index.js +3 -0
- package/lib/loop/embed/AppEmbed.d.ts +11 -3
- package/lib/loop/embed/AppEmbed.js +42 -16
- package/lib/loop/embed/element.d.ts +5 -0
- package/lib/loop/embed/element.js +61 -5
- package/lib/loop/embed/embedConfig.d.ts +8 -0
- package/lib/loop/embed/embedConfig.js +18 -0
- package/lib/loop/embed/hostBridge.d.ts +28 -2
- package/lib/loop/embed/hostBridge.js +22 -2
- package/lib/loop/index.d.ts +4 -3
- package/lib/loop/index.js +4 -3
- package/lib/loop/plugins/a2ui-surface/InlineSurface.d.ts +7 -1
- package/lib/loop/plugins/a2ui-surface/InlineSurface.js +7 -2
- package/lib/loop/plugins/a2ui-surface/SurfaceToolResult.js +4 -1
- package/lib/loop/plugins/a2ui-surface/ownComponents.d.ts +6 -0
- package/lib/loop/plugins/a2ui-surface/ownComponents.js +30 -0
- package/lib/loop/plugins/app-components/index.d.ts +26 -0
- package/lib/loop/plugins/app-components/index.js +56 -0
- package/lib/loop/plugins/app-elements/AppElements.js +4 -1
- package/lib/loop/plugins/app-page/AppPage.d.ts +9 -0
- package/lib/loop/plugins/app-page/AppPage.js +106 -8
- package/lib/loop/plugins/app-page/appPageModel.d.ts +17 -1
- package/lib/loop/plugins/app-page/appPageModel.js +120 -4
- package/lib/loop/plugins/app-page/index.d.ts +1 -0
- package/lib/loop/plugins/app-page/index.js +1 -0
- package/lib/loop/plugins/app-page/pageRun.d.ts +46 -0
- package/lib/loop/plugins/app-page/pageRun.js +111 -0
- package/lib/loop/plugins/assistant/index.js +8 -6
- package/lib/loop/plugins/chat/ChatView.js +6 -4
- package/lib/loop/plugins/chat/index.d.ts +1 -1
- package/lib/loop/plugins/chat/index.js +2 -1
- package/lib/models/PageTag.d.ts +1 -1
- package/lib/runtimes/browser/a2aPeer.d.ts +2 -1
- package/lib/specs/actions.js +78 -1
- package/lib/specs/apps.d.ts +5 -0
- package/lib/specs/apps.js +1384 -231
- package/lib/specs/appspecSchema.js +191 -1
- package/lib/specs/index.d.ts +1 -0
- package/lib/specs/index.js +1 -0
- package/lib/specs/mcpServers.js +8 -2
- package/lib/specs/scenes.d.ts +21 -0
- package/lib/specs/scenes.js +1191 -0
- package/lib/specs/teams/teams.d.ts +3 -0
- package/lib/specs/teams/teams.js +249 -0
- package/lib/types/agentspecs.d.ts +70 -0
- package/lib/types/chat.d.ts +6 -5
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/lib/types/scenes.d.ts +207 -0
- package/lib/types/scenes.js +5 -0
- package/lib/types/teams.d.ts +13 -2
- package/package.json +4 -4
- package/scripts/codegen/generate_agents.py +1 -0
- package/scripts/codegen/generate_apps.py +4 -2
- package/scripts/codegen/generate_scenes.py +301 -0
- package/scripts/codegen/generate_teams.py +15 -2
- package/lib/components/inspector/JsonTree.d.ts +0 -24
- package/lib/components/inspector/JsonTree.js +0 -89
- package/lib/components/inspector/a2aInspect.d.ts +0 -56
- package/lib/components/inspector/a2aInspect.js +0 -478
- package/lib/components/inspector/agentInspector.d.ts +0 -179
- package/lib/components/inspector/agentInspector.js +0 -267
- package/lib/components/inspector/chatInspect.d.ts +0 -38
- package/lib/components/inspector/chatInspect.js +0 -65
|
@@ -58,6 +58,7 @@ import { LoopEmbed } from '../embed/LoopEmbed';
|
|
|
58
58
|
import { dumpAppspec } from './appspec';
|
|
59
59
|
import { defineAppActivityPlugin } from '../plugins/app-activity';
|
|
60
60
|
import { AppElementsPlugin } from '../plugins/app-elements';
|
|
61
|
+
import { defineAppComponentsPlugin, hasCustomComponents, } from '../plugins/app-components';
|
|
61
62
|
import { defineAppComputerPlugin } from '../plugins/app-computer';
|
|
62
63
|
import { defineAppRulesPlugin } from '../plugins/app-rules';
|
|
63
64
|
import { defineAppComposerPlugin, needsAppComposer, startersAsOpeners, } from './AppComposer';
|
|
@@ -67,6 +68,7 @@ import { defineAppFeedbackPlugin } from './AppFeedback';
|
|
|
67
68
|
import { defineAppKeptPlugin } from './AppKept';
|
|
68
69
|
import { keepsFeedback } from './feedback';
|
|
69
70
|
import { keptBeforeFirstMessage } from './kept';
|
|
71
|
+
import { ChatLanguage } from '../../chat/ChatLanguage';
|
|
70
72
|
/** The id of an agent or a Cog, without its version. */
|
|
71
73
|
export const agentIdOf = (app) => {
|
|
72
74
|
const reference = app.agent || app.team;
|
|
@@ -167,7 +169,8 @@ export function appLayoutOptions(app) {
|
|
|
167
169
|
* whose contributions are the blocks it may draw (R-01b); a thumb and a
|
|
168
170
|
* comment on each answer when its record keeps feedback (V-18); its
|
|
169
171
|
* commands in the composer's `/` menu and its modes beside it (P-19); the
|
|
170
|
-
* side panel and the pages its code opens (`app-elements`, P-18);
|
|
172
|
+
* side panel and the pages its code opens (`app-elements`, P-18); the
|
|
173
|
+
* components its developer wrote (`app-components`, P-17); what it
|
|
171
174
|
* keeps and for how long, under its prompt before the first message, when
|
|
172
175
|
* `kept` says it (R-31); with
|
|
173
176
|
* `sidebar`, its rules and approvals card, its activity feed and its
|
|
@@ -203,6 +206,9 @@ export function appPreset(app, options = {}) {
|
|
|
203
206
|
plugins: [
|
|
204
207
|
defineAppPlugin(app, shown),
|
|
205
208
|
...(withPage ? [defineAppPagePlugin(shown), ...blocks] : []),
|
|
209
|
+
// The components its developer wrote (LOOP P-17): blocks of its page
|
|
210
|
+
// and renderers of its page, its elements and its answers — its own.
|
|
211
|
+
...(hasCustomComponents(app) ? [defineAppComponentsPlugin(app)] : []),
|
|
206
212
|
// Its commands in the composer's `/` menu, its modes and its profiles
|
|
207
213
|
// beside the composer (LOOP P-19, P-20) and what a person may attach
|
|
208
214
|
// there (P-21): only for an application that has some.
|
|
@@ -385,31 +391,34 @@ export function AppRenderer({ app, plugins = NO_PLUGINS, instance, onPresence, o
|
|
|
385
391
|
if ('problem' in preset) {
|
|
386
392
|
return (_jsx("div", { role: "status", style: { padding: 16 }, children: preset.problem }));
|
|
387
393
|
}
|
|
388
|
-
return (
|
|
389
|
-
// The
|
|
390
|
-
//
|
|
391
|
-
, {
|
|
394
|
+
return (
|
|
395
|
+
// The chat's own words in the person's language too (P-26): the host's,
|
|
396
|
+
// else the browser's.
|
|
397
|
+
_jsx(ChatLanguage, { language: language, children: _jsx(LoopEmbed
|
|
392
398
|
// The application's own agent: created under its id, so that its
|
|
393
399
|
// spec is applied to an agent of its own, never to the one it extends.
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
name:
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
400
|
+
, {
|
|
401
|
+
// The application's own agent: created under its id, so that its
|
|
402
|
+
// spec is applied to an agent of its own, never to the one it extends.
|
|
403
|
+
agentId: app.id, ...preset.workspace, ...(frame
|
|
404
|
+
? {
|
|
405
|
+
frameTitle: [app.emoji, shown.name].filter(Boolean).join(' '),
|
|
406
|
+
}
|
|
407
|
+
: {}),
|
|
408
|
+
// Its rules, activity and computer on a rail, one at a time (T-07).
|
|
409
|
+
sidebarRail: sidebar || computer, teamPicker: false, showAgentVariants: false, graph: false, pluginsPanel: false, datalayerAgentSpecId: DATALAYER_BOOTSTRAP_AGENTSPEC,
|
|
410
|
+
// Where an application runs is decided by its host — the Studio's
|
|
411
|
+
// Preview, the hosted page — not offered to its user.
|
|
412
|
+
targetFixed: true, ...(named ? { themeVariant: named.variant } : {}), ...(namedMode ? { colorMode: namedMode } : {}), themeOverrides: themeOverrides,
|
|
413
|
+
// Its own face, name and welcome in the chat (T-08); no counters: a
|
|
414
|
+
// person using an application is not asking about tokens.
|
|
415
|
+
presence: {
|
|
416
|
+
name: shown.name,
|
|
417
|
+
face: app.emoji,
|
|
418
|
+
welcome: shown.interface.welcome || shown.description,
|
|
419
|
+
paused,
|
|
420
|
+
onPresence,
|
|
421
|
+
onSaying,
|
|
422
|
+
}, showTokenUsage: false, datalayerCreatePayload: datalayerCreatePayload, ...embed, plugins: allPlugins }) }));
|
|
414
423
|
}
|
|
415
424
|
export default AppRenderer;
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
*
|
|
25
25
|
* @module loop/apps/appspec
|
|
26
26
|
*/
|
|
27
|
-
import type { AppAccent, AppThemeMode, AppThemeVariant, AppAssistantCharacter, AppEmbedMode, AppVoiceSpec, AppKind, AppLayout, AppSpec } from '../../types/agentspecs';
|
|
27
|
+
import type { AppAccent, AppThemeMode, AppThemeVariant, AppAssistantCharacter, AppEmbedMode, AppCustomComponentSpec, AppVoiceSpec, AppKind, AppLayout, AppPageOutputComponent, 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 language an application's own words are in, unless it says (LOOP P-26). */
|
|
@@ -62,6 +62,16 @@ export declare function retentionDays(keepFor: string): number;
|
|
|
62
62
|
export declare function emptyAppspec(kind?: AppKind): AppSpec;
|
|
63
63
|
/** An application's voice (VOICE.md VO-41): off unless said. */
|
|
64
64
|
export declare const DEFAULT_VOICE: AppVoiceSpec;
|
|
65
|
+
/** What a page's output may be drawn with (LOOP P-05), and the property of each that shows its value. */
|
|
66
|
+
export declare const PAGE_OUTPUT_SHOWS: Record<AppPageOutputComponent, string>;
|
|
67
|
+
/** The height of a component its developer wrote (P-17), unless it says. */
|
|
68
|
+
export declare const DEFAULT_CUSTOM_HEIGHT = 240;
|
|
69
|
+
/**
|
|
70
|
+
* The property a page's output fills with its value (P-05): a catalog
|
|
71
|
+
* component's (`PAGE_OUTPUT_SHOWS`), or what a component of the
|
|
72
|
+
* application's own shows first (P-17). Undefined for any other.
|
|
73
|
+
*/
|
|
74
|
+
export declare function pageOutputShows(component: string, own?: readonly AppCustomComponentSpec[]): string | undefined;
|
|
65
75
|
/** What reading a document gives: the application, and what the document got wrong. */
|
|
66
76
|
export interface ParsedAppspec {
|
|
67
77
|
app: AppSpec;
|
package/lib/loop/apps/appspec.js
CHANGED
|
@@ -363,6 +363,103 @@ function parseUploads(data) {
|
|
|
363
363
|
maxFiles: numberOf(data.max_files, DEFAULT_MAX_FILES),
|
|
364
364
|
};
|
|
365
365
|
}
|
|
366
|
+
/** What a page's output may be drawn with (LOOP P-05), and the property of each that shows its value. */
|
|
367
|
+
export const PAGE_OUTPUT_SHOWS = {
|
|
368
|
+
Text: 'text',
|
|
369
|
+
Image: 'url',
|
|
370
|
+
Table: 'rows',
|
|
371
|
+
Chart: 'points',
|
|
372
|
+
};
|
|
373
|
+
/** The height of a component its developer wrote (P-17), unless it says. */
|
|
374
|
+
export const DEFAULT_CUSTOM_HEIGHT = 240;
|
|
375
|
+
/**
|
|
376
|
+
* A component its developer wrote (LOOP P-17), as written: its schema and
|
|
377
|
+
* example kept whole, so that the checks say what is wrong with them.
|
|
378
|
+
*/
|
|
379
|
+
function parseCustomComponent(data) {
|
|
380
|
+
const component = {
|
|
381
|
+
name: text(data.name),
|
|
382
|
+
description: text(data.description),
|
|
383
|
+
props: isData(data.props)
|
|
384
|
+
? JSON.parse(JSON.stringify(data.props))
|
|
385
|
+
: { type: 'object', properties: {} },
|
|
386
|
+
shows: texts(data.shows),
|
|
387
|
+
sends: texts(data.sends),
|
|
388
|
+
source: text(data.source),
|
|
389
|
+
integrity: text(data.integrity),
|
|
390
|
+
height: numberOf(data.height, DEFAULT_CUSTOM_HEIGHT),
|
|
391
|
+
};
|
|
392
|
+
if (isData(data.example)) {
|
|
393
|
+
component.example = JSON.parse(JSON.stringify(data.example));
|
|
394
|
+
}
|
|
395
|
+
return component;
|
|
396
|
+
}
|
|
397
|
+
function dumpCustomComponent(component) {
|
|
398
|
+
const written = new Writer()
|
|
399
|
+
.text('name', component.name, '\u0000')
|
|
400
|
+
.text('description', component.description, '\u0000');
|
|
401
|
+
written.data.props = JSON.parse(JSON.stringify(component.props));
|
|
402
|
+
written
|
|
403
|
+
.list('shows', component.shows)
|
|
404
|
+
.list('sends', component.sends)
|
|
405
|
+
.text('source', component.source, '\u0000')
|
|
406
|
+
.text('integrity', component.integrity)
|
|
407
|
+
.value('height', component.height, DEFAULT_CUSTOM_HEIGHT);
|
|
408
|
+
if (component.example) {
|
|
409
|
+
written.data.example = JSON.parse(JSON.stringify(component.example));
|
|
410
|
+
}
|
|
411
|
+
return written.data;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* The property a page's output fills with its value (P-05): a catalog
|
|
415
|
+
* component's (`PAGE_OUTPUT_SHOWS`), or what a component of the
|
|
416
|
+
* application's own shows first (P-17). Undefined for any other.
|
|
417
|
+
*/
|
|
418
|
+
export function pageOutputShows(component, own = []) {
|
|
419
|
+
if (component in PAGE_OUTPUT_SHOWS) {
|
|
420
|
+
return PAGE_OUTPUT_SHOWS[component];
|
|
421
|
+
}
|
|
422
|
+
return own.find(each => each.name === component)?.shows[0];
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* A widget's page written in its code (P-05), as written: its inputs a form
|
|
426
|
+
* kept whole, an output drawn with another component kept as written so that
|
|
427
|
+
* the checks say it.
|
|
428
|
+
*/
|
|
429
|
+
function parsePage(data) {
|
|
430
|
+
const page = {
|
|
431
|
+
function: text(data.function),
|
|
432
|
+
inputs: (isData(data.inputs)
|
|
433
|
+
? JSON.parse(JSON.stringify(data.inputs))
|
|
434
|
+
: {}),
|
|
435
|
+
outputs: records(data.outputs).map(output => ({
|
|
436
|
+
name: text(output.name),
|
|
437
|
+
title: text(output.title),
|
|
438
|
+
component: text(output.component, 'Text'),
|
|
439
|
+
props: isData(output.props)
|
|
440
|
+
? JSON.parse(JSON.stringify(output.props))
|
|
441
|
+
: {},
|
|
442
|
+
})),
|
|
443
|
+
live: data.live !== false,
|
|
444
|
+
};
|
|
445
|
+
if (isData(data.inputs_ui)) {
|
|
446
|
+
page.inputsUi = JSON.parse(JSON.stringify(data.inputs_ui));
|
|
447
|
+
}
|
|
448
|
+
return page;
|
|
449
|
+
}
|
|
450
|
+
function dumpPage(page) {
|
|
451
|
+
const written = new Writer().text('function', page.function, '\u0000');
|
|
452
|
+
written.data.inputs = JSON.parse(JSON.stringify(page.inputs));
|
|
453
|
+
if (page.inputsUi) {
|
|
454
|
+
written.data.inputs_ui = { ...page.inputsUi };
|
|
455
|
+
}
|
|
456
|
+
written.list('outputs', page.outputs.map(output => new Writer()
|
|
457
|
+
.text('name', output.name, '\u0000')
|
|
458
|
+
.text('title', output.title)
|
|
459
|
+
.text('component', output.component, 'Text')
|
|
460
|
+
.part('props', { ...output.props }).data));
|
|
461
|
+
return written.value('live', page.live, true).data;
|
|
462
|
+
}
|
|
366
463
|
function parseInterface(data, kind) {
|
|
367
464
|
const parsed = {
|
|
368
465
|
layout: oneOf(data.layout, ['chat', 'page', 'split'], DEFAULT_LAYOUTS[kind]),
|
|
@@ -392,9 +489,16 @@ function parseInterface(data, kind) {
|
|
|
392
489
|
if (isData(data.uploads)) {
|
|
393
490
|
parsed.uploads = parseUploads(data.uploads);
|
|
394
491
|
}
|
|
492
|
+
// Components its developer wrote (P-17): absent when it has none.
|
|
493
|
+
if (Array.isArray(data.custom_components)) {
|
|
494
|
+
parsed.customComponents = records(data.custom_components).map(parseCustomComponent);
|
|
495
|
+
}
|
|
395
496
|
if (isData(data.surface)) {
|
|
396
497
|
parsed.surface = parseSurface(data.surface);
|
|
397
498
|
}
|
|
499
|
+
if (isData(data.page)) {
|
|
500
|
+
parsed.page = parsePage(data.page);
|
|
501
|
+
}
|
|
398
502
|
if (isAssistantCharacterId(data.assistant)) {
|
|
399
503
|
parsed.assistant = data.assistant;
|
|
400
504
|
}
|
|
@@ -452,6 +556,11 @@ function parseDeployment(data) {
|
|
|
452
556
|
? {
|
|
453
557
|
host: {
|
|
454
558
|
context: texts(data.embedded.host.context),
|
|
559
|
+
// Kept as written when said: a value that is none is refused
|
|
560
|
+
// by the checks, never read as `claimed` (D-21).
|
|
561
|
+
...(data.embedded.host.user !== undefined
|
|
562
|
+
? { user: text(data.embedded.host.user) }
|
|
563
|
+
: {}),
|
|
455
564
|
functions: records(data.embedded.host.functions).map(fn => ({
|
|
456
565
|
name: text(fn.name),
|
|
457
566
|
description: text(fn.description),
|
|
@@ -752,9 +861,13 @@ function dumpInterface(spec, kind) {
|
|
|
752
861
|
.value('max_files', spec.uploads.maxFiles, DEFAULT_MAX_FILES).data;
|
|
753
862
|
}
|
|
754
863
|
writer.list('components', spec.components);
|
|
864
|
+
writer.list('custom_components', (spec.customComponents ?? []).map(dumpCustomComponent));
|
|
755
865
|
if (spec.surface) {
|
|
756
866
|
writer.data.surface = dumpSurface(spec.surface);
|
|
757
867
|
}
|
|
868
|
+
if (spec.page) {
|
|
869
|
+
writer.data.page = dumpPage(spec.page);
|
|
870
|
+
}
|
|
758
871
|
if (spec.assistant) {
|
|
759
872
|
writer.data.assistant = spec.assistant;
|
|
760
873
|
}
|
|
@@ -905,7 +1018,8 @@ export function dumpAppspec(app) {
|
|
|
905
1018
|
JSON.stringify(NO_HOST_PARAMETERS)
|
|
906
1019
|
? {}
|
|
907
1020
|
: { parameters: fn.parameters }),
|
|
908
|
-
})))
|
|
1021
|
+
})))
|
|
1022
|
+
.value('user', app.deployment.embedded.host.user ?? 'claimed', 'claimed').data).data).data)
|
|
909
1023
|
.text('goal', app.goal)
|
|
910
1024
|
.list('triggers', app.triggers.map(trigger => new Writer()
|
|
911
1025
|
.text('type', trigger.type, '\u0000')
|
|
@@ -20,6 +20,14 @@ export declare const componentNamed: (name: string) => ComponentSpec | undefined
|
|
|
20
20
|
* once; a tool says what it does, by class, each class once.
|
|
21
21
|
*/
|
|
22
22
|
export declare function ownCodeProblems(app: AppSpec): string[];
|
|
23
|
+
/**
|
|
24
|
+
* What agentspecs refuses of a widget's page written in its code (LOOP
|
|
25
|
+
* P-05): a page that is not a widget's, inputs that are not a form, outputs
|
|
26
|
+
* of one name twice, drawn with another component, saying in their `props`
|
|
27
|
+
* what their value fills or lacking what their component needs, and an input
|
|
28
|
+
* named as a setting — both are on the page at `/inputs/<name>`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function pageProblems(app: AppSpec): string[];
|
|
23
31
|
/** How an organization's own context is named: never a catalogue id (agentspecs' `ORGANIZATION_FRAME_PREFIX`). */
|
|
24
32
|
/**
|
|
25
33
|
* What stops a Form block from asking (LOOP C-16), in agentspecs'
|
package/lib/loop/apps/checks.js
CHANGED
|
@@ -39,12 +39,13 @@ import { getTeamSpec } from '../../specs/teams';
|
|
|
39
39
|
import { getBackendToolSpec } from '../../specs/backendTools';
|
|
40
40
|
import { getTrack } from '../../specs/tracks';
|
|
41
41
|
import { pluginsOffSetupNotes } from '../plugins/canvas-blocks';
|
|
42
|
-
import { APP_THEME_MODES, APP_THEME_VARIANTS, isAssistantCharacterId, parseAppspec, } from './appspec';
|
|
42
|
+
import { APP_THEME_MODES, APP_THEME_VARIANTS, isAssistantCharacterId, PAGE_OUTPUT_SHOWS, parseAppspec, } from './appspec';
|
|
43
43
|
import { classesOf, splitRef, toolBehaviours } from './rules';
|
|
44
44
|
import { COMMAND_INPUT, COMMAND_NAME, MODE_ID } from './composer';
|
|
45
|
-
import { HOST_NAME, hostToolsOf } from './hostTools';
|
|
45
|
+
import { HOST_NAME, HOST_USERS, hostToolsOf } from './hostTools';
|
|
46
46
|
import { MAX_UPLOAD_MB, UPLOAD_KIND } from './uploads';
|
|
47
47
|
import { formUiProblems } from './settingsInputs';
|
|
48
|
+
import { customComponentOf, customComponentsProblems, customNodeRefused, customPropsRefused, } from './customComponents';
|
|
48
49
|
import { translationProblems } from './language';
|
|
49
50
|
export const NOT_READY = 'Not ready';
|
|
50
51
|
export const NEEDS_ATTENTION = 'Needs attention';
|
|
@@ -129,6 +130,82 @@ export function ownCodeProblems(app) {
|
|
|
129
130
|
if (new Set(decided).size !== decided.length) {
|
|
130
131
|
problems.push('Two tests are decided by the same function of its code.');
|
|
131
132
|
}
|
|
133
|
+
problems.push(...pageProblems(app));
|
|
134
|
+
// The components its developer wrote, reviewed as the catalog's (P-17).
|
|
135
|
+
problems.push(...customComponentsProblems(app));
|
|
136
|
+
return problems;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* What agentspecs refuses of a widget's page written in its code (LOOP
|
|
140
|
+
* P-05): a page that is not a widget's, inputs that are not a form, outputs
|
|
141
|
+
* of one name twice, drawn with another component, saying in their `props`
|
|
142
|
+
* what their value fills or lacking what their component needs, and an input
|
|
143
|
+
* named as a setting — both are on the page at `/inputs/<name>`.
|
|
144
|
+
*/
|
|
145
|
+
export function pageProblems(app) {
|
|
146
|
+
const page = app.interface.page;
|
|
147
|
+
if (!page) {
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
const problems = [];
|
|
151
|
+
if (app.kind !== 'widget') {
|
|
152
|
+
problems.push(`A page of inputs and outputs is a widget's: a ${app.kind} application has none. Remove \`interface.page\`, or make it a widget.`);
|
|
153
|
+
}
|
|
154
|
+
if (!CODE_NAME.test(page.function)) {
|
|
155
|
+
problems.push(`Cannot use “${page.function}” as the function of its page: letters, digits and \`_\`, as in Python.`);
|
|
156
|
+
}
|
|
157
|
+
problems.push(...formProblems({
|
|
158
|
+
id: 'page inputs',
|
|
159
|
+
schema: page.inputs,
|
|
160
|
+
ui: page.inputsUi,
|
|
161
|
+
}));
|
|
162
|
+
if (page.outputs.length === 0) {
|
|
163
|
+
problems.push('Its page shows one output at least.');
|
|
164
|
+
}
|
|
165
|
+
const names = page.outputs.map(output => output.name);
|
|
166
|
+
if (new Set(names).size !== names.length) {
|
|
167
|
+
problems.push('Two outputs of its page have the same name.');
|
|
168
|
+
}
|
|
169
|
+
for (const output of page.outputs) {
|
|
170
|
+
if (!CODE_NAME.test(output.name)) {
|
|
171
|
+
problems.push(`Cannot use “${output.name}” as an output's name: letters, digits and \`_\`, as in Python.`);
|
|
172
|
+
}
|
|
173
|
+
// A component its developer wrote (P-17): its value what it shows first.
|
|
174
|
+
const custom = output.component in PAGE_OUTPUT_SHOWS
|
|
175
|
+
? undefined
|
|
176
|
+
: customComponentOf(app, output.component);
|
|
177
|
+
if (custom) {
|
|
178
|
+
if (custom.shows.length === 0) {
|
|
179
|
+
problems.push(`The output “${output.name}” is drawn with ${output.component}, which shows nothing: its value is what it shows first, under \`shows\`.`);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const refused = customPropsRefused(custom, output.props);
|
|
183
|
+
if (refused.length > 0) {
|
|
184
|
+
problems.push(`The output “${output.name}” is a ${output.component} its schema refuses: ${refused.join('; ')}.`);
|
|
185
|
+
}
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const shows = PAGE_OUTPUT_SHOWS[output.component];
|
|
189
|
+
if (!shows) {
|
|
190
|
+
problems.push(`The output “${output.name}” is drawn with ${output.component}: an output is one of ${Object.keys(PAGE_OUTPUT_SHOWS).join(', ')} or a component of its own (\`custom_components\`).`);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const said = [shows, 'id', 'component'].filter(key => key in output.props);
|
|
194
|
+
if (said.length > 0) {
|
|
195
|
+
problems.push(`The output “${output.name}” says ${said.join(', ')} in its \`props\`: its id is its name, and what it shows is its value.`);
|
|
196
|
+
}
|
|
197
|
+
const schema = getComponent(output.component)?.properties;
|
|
198
|
+
const missing = (schema?.required ?? []).filter(key => key !== shows && !(key in output.props));
|
|
199
|
+
if (missing.length > 0) {
|
|
200
|
+
problems.push(`The output “${output.name}” is a ${output.component} without ${missing.join(', ')}: say it in its \`props\`.`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const settings = Object.keys(app.interface.settings?.properties ?? {});
|
|
204
|
+
const inputs = Object.keys(page.inputs?.properties ?? {});
|
|
205
|
+
const shared = settings.filter(name => inputs.includes(name));
|
|
206
|
+
if (shared.length > 0) {
|
|
207
|
+
problems.push(`${shared.map(name => `“${name}”`).join(', ')} is both a setting and an input of its page: name one otherwise.`);
|
|
208
|
+
}
|
|
132
209
|
return problems;
|
|
133
210
|
}
|
|
134
211
|
/** What the spec says of itself that the tolerant reader lets through. */
|
|
@@ -228,6 +305,10 @@ function shapeProblems(app) {
|
|
|
228
305
|
if (new Set(names).size !== names.length) {
|
|
229
306
|
problems.push('The host’s functions are named once each.');
|
|
230
307
|
}
|
|
308
|
+
// Who its user is (D-21): what the page says, or what its server signed.
|
|
309
|
+
if (host.user !== undefined && !HOST_USERS.includes(host.user)) {
|
|
310
|
+
problems.push(`“${host.user}” is not how the host’s user is taken: claimed or signed.`);
|
|
311
|
+
}
|
|
231
312
|
const named = new Set(app.rules.flatMap(rule => rule.appliesTo));
|
|
232
313
|
for (const tool of hostToolsOf(host)) {
|
|
233
314
|
if (!named.has(tool)) {
|
|
@@ -380,13 +461,21 @@ function referenceProblems(app, organizationFrames) {
|
|
|
380
461
|
}
|
|
381
462
|
}
|
|
382
463
|
// The components it may use, and those its surface uses, are the catalog's (C-13).
|
|
464
|
+
// Those its developer wrote are of the catalog for it alone (P-17).
|
|
383
465
|
for (const name of app.interface.components ?? []) {
|
|
384
|
-
if (!componentNamed(name)) {
|
|
466
|
+
if (!componentNamed(name) && !customComponentOf(app, name)) {
|
|
385
467
|
problems.push(`There is no component named “${name}” in the catalog.`);
|
|
386
468
|
}
|
|
387
469
|
}
|
|
388
470
|
for (const node of app.interface.surface?.components ?? []) {
|
|
389
|
-
|
|
471
|
+
const custom = customComponentOf(app, String(node.component));
|
|
472
|
+
if (custom) {
|
|
473
|
+
const refused = customNodeRefused(custom, node);
|
|
474
|
+
if (refused.length > 0) {
|
|
475
|
+
problems.push(`The surface's “${String(node.id)}” is a ${custom.name} its schema refuses: ${refused.join('; ')}.`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
else if (!componentNamed(String(node.component))) {
|
|
390
479
|
problems.push(`The surface's “${String(node.id)}” is a “${String(node.component)}”, which the catalog does not have.`);
|
|
391
480
|
}
|
|
392
481
|
else if (node.component === 'Form') {
|
|
@@ -912,6 +1001,19 @@ export function documentShapeProblems(document) {
|
|
|
912
1001
|
required(item, 'id', where);
|
|
913
1002
|
required(item, 'component', where);
|
|
914
1003
|
});
|
|
1004
|
+
}); // A widget's page written in its code (LOOP P-05), as agentspecs reads it.
|
|
1005
|
+
mapping(ui.page, 'interface.page', page => {
|
|
1006
|
+
unknownKeys(page, ['function', 'inputs', 'inputs_ui', 'outputs', 'live'], 'interface.page', 'a page');
|
|
1007
|
+
required(page, 'function', 'interface.page');
|
|
1008
|
+
required(page, 'inputs', 'interface.page');
|
|
1009
|
+
required(page, 'outputs', 'interface.page');
|
|
1010
|
+
if (page.live !== undefined && typeof page.live !== 'boolean') {
|
|
1011
|
+
at('interface.page.live', 'is true or false');
|
|
1012
|
+
}
|
|
1013
|
+
records(page.outputs, 'interface.page.outputs', (output, where) => {
|
|
1014
|
+
required(output, 'name', where);
|
|
1015
|
+
unknownKeys(output, ['name', 'title', 'component', 'props'], where, 'an output');
|
|
1016
|
+
});
|
|
915
1017
|
});
|
|
916
1018
|
});
|
|
917
1019
|
mapping(d.tests, 'tests', tests => {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { AppCustomComponentSpec, AppSpec, ComponentSpec } from '../../types/agentspecs';
|
|
2
|
+
/** Its name: a word starting with a capital letter, as the catalog's are named. */
|
|
3
|
+
export declare const CUSTOM_COMPONENT_NAME: RegExp;
|
|
4
|
+
/** What one of its properties may be typed: what the renderer tells apart. */
|
|
5
|
+
export declare const CUSTOM_PROPERTY_TYPES: readonly ["string", "integer", "number", "boolean", "array", "object"];
|
|
6
|
+
/** What every component of a surface already has, and a custom one cannot declare. */
|
|
7
|
+
export declare const CUSTOM_RESERVED_NAMES: readonly ["id", "component", "action", "weight", "visible_when", "children", "child"];
|
|
8
|
+
/**
|
|
9
|
+
* Where its module is loaded from: an address served over HTTPS, or this
|
|
10
|
+
* machine while it is written. A file of the application's folder waits for
|
|
11
|
+
* its packaging (P-29).
|
|
12
|
+
*/
|
|
13
|
+
export declare const CUSTOM_SOURCE: RegExp;
|
|
14
|
+
/** A Subresource Integrity hash: the module as it was reviewed. */
|
|
15
|
+
export declare const INTEGRITY: RegExp;
|
|
16
|
+
/**
|
|
17
|
+
* What its schema refuses of the properties given, in the words agentspecs
|
|
18
|
+
* says them: one it does not have, a value of another type, one it needs.
|
|
19
|
+
* `bound` are given elsewhere — bound to what the application publishes.
|
|
20
|
+
*/
|
|
21
|
+
export declare function customPropsRefused(component: AppCustomComponentSpec, props: Record<string, unknown>, bound?: readonly string[]): string[];
|
|
22
|
+
/** What stops one component its developer wrote from being reviewed, in sentences. */
|
|
23
|
+
export declare function customComponentProblems(component: AppCustomComponentSpec): string[];
|
|
24
|
+
/** What stops the components an application's developer wrote, in sentences. */
|
|
25
|
+
export declare function customComponentsProblems(app: AppSpec): string[];
|
|
26
|
+
/** A component its developer wrote, by name, or undefined. */
|
|
27
|
+
export declare function customComponentOf(app: Pick<AppSpec, 'interface'>, name: string): AppCustomComponentSpec | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* It as the catalog lists a component (C-13): what the palette, the
|
|
30
|
+
* components page and the checks read — under the application's version, its
|
|
31
|
+
* category `custom`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function customComponentEntry(component: AppCustomComponentSpec, version: string): ComponentSpec;
|
|
34
|
+
/**
|
|
35
|
+
* The components an application may place: the catalog's own, given, and
|
|
36
|
+
* those its developer wrote, for it alone. A name of its own that the
|
|
37
|
+
* catalog has is not listed twice: the checks refuse it.
|
|
38
|
+
*/
|
|
39
|
+
export declare function appComponents(app: Pick<AppSpec, 'interface' | 'version'>, catalog: readonly ComponentSpec[]): ComponentSpec[];
|
|
40
|
+
/** What its schema refuses of a node of a surface drawn by it, in sentences; empty when nothing. */
|
|
41
|
+
export declare function customNodeRefused(component: AppCustomComponentSpec, node: Record<string, unknown>): string[];
|