@datalayer/agent-runtimes 1.3.29 → 1.3.30

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.
Files changed (52) hide show
  1. package/lib/AgentNode.js +14 -33
  2. package/lib/chat/base/ChatBase.js +41 -32
  3. package/lib/chat/base/modelChoice.d.ts +13 -0
  4. package/lib/chat/base/modelChoice.js +13 -0
  5. package/lib/loop/apps/AppRenderer.d.ts +33 -1
  6. package/lib/loop/apps/AppRenderer.js +96 -29
  7. package/lib/loop/apps/agent.d.ts +15 -0
  8. package/lib/loop/apps/agent.js +42 -0
  9. package/lib/loop/apps/index.d.ts +1 -0
  10. package/lib/loop/apps/index.js +1 -0
  11. package/lib/loop/core/index.d.ts +8 -0
  12. package/lib/loop/embed/AppEmbed.d.ts +42 -0
  13. package/lib/loop/embed/AppEmbed.js +205 -0
  14. package/lib/loop/embed/datalayer-app.d.ts +1 -0
  15. package/lib/loop/embed/datalayer-app.js +16 -0
  16. package/lib/loop/embed/element.d.ts +57 -0
  17. package/lib/loop/embed/element.js +405 -0
  18. package/lib/loop/embed/embedConfig.d.ts +120 -0
  19. package/lib/loop/embed/embedConfig.js +148 -0
  20. package/lib/loop/embed/embedTheme.d.ts +24 -0
  21. package/lib/loop/embed/embedTheme.js +83 -0
  22. package/lib/loop/index.d.ts +6 -2
  23. package/lib/loop/index.js +8 -2
  24. package/lib/loop/plugins/a2ui-surface/InlineSurface.d.ts +21 -3
  25. package/lib/loop/plugins/a2ui-surface/InlineSurface.js +34 -6
  26. package/lib/loop/plugins/agent-capacity/index.d.ts +4 -0
  27. package/lib/loop/plugins/agent-capacity/index.js +3 -1
  28. package/lib/loop/plugins/agents/switchable.d.ts +7 -0
  29. package/lib/loop/plugins/agents/switchable.js +91 -88
  30. package/lib/loop/plugins/app-page/AppPage.d.ts +16 -0
  31. package/lib/loop/plugins/app-page/AppPage.js +53 -0
  32. package/lib/loop/plugins/app-page/appPage.d.ts +180 -0
  33. package/lib/loop/plugins/app-page/appPage.js +443 -0
  34. package/lib/loop/plugins/app-page/index.d.ts +8 -0
  35. package/lib/loop/plugins/app-page/index.js +34 -0
  36. package/lib/loop/plugins/chat/ChatView.js +47 -25
  37. package/lib/loop/plugins/page-layout/SplitLayout.d.ts +31 -0
  38. package/lib/loop/plugins/page-layout/SplitLayout.js +129 -0
  39. package/lib/loop/plugins/page-layout/index.d.ts +13 -0
  40. package/lib/loop/plugins/page-layout/index.js +10 -1
  41. package/lib/loop/presets.d.ts +12 -0
  42. package/lib/loop/presets.js +5 -3
  43. package/lib/specs/actions.js +8 -0
  44. package/lib/specs/agents/agents.js +4 -0
  45. package/lib/specs/apps.d.ts +10 -1
  46. package/lib/specs/apps.js +1792 -178
  47. package/lib/specs/appspecSchema.js +1 -1
  48. package/lib/types/agentspecs.d.ts +11 -0
  49. package/lib/types/config.d.ts +12 -0
  50. package/package.json +2 -1
  51. package/scripts/codegen/generate_agents.py +50 -2
  52. package/scripts/codegen/generate_apps.py +36 -6
@@ -0,0 +1,129 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useRef, useState } from 'react';
3
+ import { Box } from '@datalayer/primer-addons';
4
+ /** The conversation's share of the width when the split opens. */
5
+ export const SPLIT_DEFAULT_SHARE = 0.4;
6
+ /** The narrowest and widest the conversation may be made. */
7
+ export const SPLIT_MIN_SHARE = 0.2;
8
+ export const SPLIT_MAX_SHARE = 0.8;
9
+ /** How far an arrow key moves the hairline. */
10
+ export const SPLIT_KEY_STEP = 0.05;
11
+ /** A share, held within the bounds. */
12
+ export function clampSplitShare(share) {
13
+ if (!Number.isFinite(share)) {
14
+ return SPLIT_DEFAULT_SHARE;
15
+ }
16
+ return Math.min(SPLIT_MAX_SHARE, Math.max(SPLIT_MIN_SHARE, share));
17
+ }
18
+ /** Where a key moves the hairline, or `null` for a key it ignores. */
19
+ export function splitShareForKey(share, key) {
20
+ switch (key) {
21
+ case 'ArrowLeft':
22
+ return clampSplitShare(share - SPLIT_KEY_STEP);
23
+ case 'ArrowRight':
24
+ return clampSplitShare(share + SPLIT_KEY_STEP);
25
+ case 'Home':
26
+ return SPLIT_MIN_SHARE;
27
+ case 'End':
28
+ return SPLIT_MAX_SHARE;
29
+ default:
30
+ return null;
31
+ }
32
+ }
33
+ export function SplitLayout({ editors, hasEditor, transcript, prompt, chips, picker, transient, }) {
34
+ const [share, setShare] = useState(SPLIT_DEFAULT_SHARE);
35
+ const row = useRef(null);
36
+ const dragging = useRef(false);
37
+ const shareAt = useCallback((clientX) => {
38
+ const box = row.current?.getBoundingClientRect();
39
+ if (!box || box.width <= 0) {
40
+ return null;
41
+ }
42
+ return clampSplitShare((clientX - box.left) / box.width);
43
+ }, []);
44
+ const onPointerDown = (event) => {
45
+ dragging.current = true;
46
+ event.currentTarget.setPointerCapture?.(event.pointerId);
47
+ event.preventDefault();
48
+ };
49
+ const onPointerMove = (event) => {
50
+ if (!dragging.current) {
51
+ return;
52
+ }
53
+ const next = shareAt(event.clientX);
54
+ if (next !== null) {
55
+ setShare(next);
56
+ }
57
+ };
58
+ const onPointerUp = (event) => {
59
+ dragging.current = false;
60
+ event.currentTarget.releasePointerCapture?.(event.pointerId);
61
+ };
62
+ const onKeyDown = (event) => {
63
+ const next = splitShareForKey(share, event.key);
64
+ if (next !== null) {
65
+ event.preventDefault();
66
+ setShare(next);
67
+ }
68
+ };
69
+ const percent = Math.round(share * 100);
70
+ return (_jsxs(Box, { "data-testid": "loop-split-layout", sx: {
71
+ flex: '1 1 auto',
72
+ minHeight: 0,
73
+ display: 'flex',
74
+ flexDirection: 'column',
75
+ }, children: [picker, _jsxs(Box, { ref: row, sx: {
76
+ flex: '1 1 auto',
77
+ minHeight: 0,
78
+ display: 'flex',
79
+ // The hidden editors position themselves against this row.
80
+ position: 'relative',
81
+ overflow: 'hidden',
82
+ }, children: [_jsxs(Box, { "data-testid": "loop-split-conversation", sx: {
83
+ flex: hasEditor ? `0 0 ${percent}%` : '1 1 auto',
84
+ minWidth: 0,
85
+ minHeight: 0,
86
+ display: 'flex',
87
+ flexDirection: 'column',
88
+ // The transcript draws a rule on its left when an editor is on
89
+ // screen, for the split it sits on the right of. Here it is on
90
+ // the left, and the hairline is the separator: the rule is
91
+ // pushed out of the row, which clips it.
92
+ ml: hasEditor ? '-1px' : 0,
93
+ }, children: [transcript, chips, prompt] }), hasEditor ? (_jsx(Box, { role: "separator", "aria-orientation": "vertical", "aria-label": "Resize the conversation and the page", "aria-valuemin": Math.round(SPLIT_MIN_SHARE * 100), "aria-valuemax": Math.round(SPLIT_MAX_SHARE * 100), "aria-valuenow": percent, tabIndex: 0, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onPointerCancel: onPointerUp, onKeyDown: onKeyDown, sx: {
94
+ // A hairline to the eye, a few pixels to the pointer.
95
+ flex: '0 0 7px',
96
+ mx: '-3px',
97
+ zIndex: 1,
98
+ cursor: 'col-resize',
99
+ touchAction: 'none',
100
+ display: 'flex',
101
+ justifyContent: 'center',
102
+ outline: 'none',
103
+ '&::before': {
104
+ content: '""',
105
+ width: '1px',
106
+ height: '100%',
107
+ bg: 'border.default',
108
+ },
109
+ '&:hover::before, &:focus-visible::before': {
110
+ width: '3px',
111
+ bg: 'accent.emphasis',
112
+ },
113
+ } })) : null, _jsx(Box, { "data-testid": "loop-split-page", sx: hasEditor
114
+ ? {
115
+ flex: '1 1 0',
116
+ minWidth: 0,
117
+ minHeight: 0,
118
+ display: 'flex',
119
+ }
120
+ : // Mounted but out of sight: the editors keep their place.
121
+ {
122
+ position: 'absolute',
123
+ inset: 0,
124
+ visibility: 'hidden',
125
+ pointerEvents: 'none',
126
+ zIndex: -1,
127
+ }, children: editors })] }), transient] }));
128
+ }
129
+ export default SplitLayout;
@@ -3,10 +3,23 @@ import type { ChatViewMode } from '../../../types/chat';
3
3
  import { type TurnPanelFooter } from './TurnPanel';
4
4
  export { pageLayoutPanelOpen, pageLayoutSheet, openPagePanel as openConversationPanel, } from '@datalayer/primer-addons/lib/reactor';
5
5
  export type { TurnPanelFooter } from './TurnPanel';
6
+ export { SplitLayout, SPLIT_DEFAULT_SHARE, SPLIT_KEY_STEP, SPLIT_MAX_SHARE, SPLIT_MIN_SHARE, clampSplitShare, splitShareForKey, } from './SplitLayout';
6
7
  export type { PageSize, PageSizeFormat, } from '@datalayer/primer-addons/lib/reactor';
7
8
  export declare const LOOP_PAGE_LAYOUT_PLUGIN_NAME = "@datalayer/loop-plugin-page-layout";
8
9
  /** What a host may set on the page layout. */
9
10
  export type LoopPageLayoutConfig = {
11
+ /**
12
+ * How the parts are arranged.
13
+ *
14
+ * `page` (the default): the work on a centred sheet, the composer over
15
+ * it, the conversation in a panel that opens when wanted. `split`: the
16
+ * conversation and the work side by side, the conversation on the left
17
+ * with the composer under it, one hairline between them that a person
18
+ * drags (`SplitLayout`) — an Appspec's `layout: split`. The split keeps
19
+ * the conversation on screen, so it has no turn panel, no conversation
20
+ * toggle and no display modes; the other settings are the page's.
21
+ */
22
+ arrangement?: 'page' | 'split';
10
23
  /**
11
24
  * Where the turn panel hangs on the composer.
12
25
  *
@@ -6,12 +6,14 @@ import { PageLayout, PagePanelToggle, openPagePanel, } from '@datalayer/primer-a
6
6
  import { ChatViewModeToggle } from '../../../chat/header/ChatViewModeToggle';
7
7
  import { LoopChatLayout, LoopChatTurn, LoopPromptPanel, LoopSlots, } from '../../core';
8
8
  import { TurnPanel } from './TurnPanel';
9
+ import { SplitLayout } from './SplitLayout';
9
10
  /*
10
11
  * The layout's state is primer-addons', re-exported under the names the
11
12
  * Loop always had: a host that opened the conversation panel by calling
12
13
  * `openConversationPanel()` keeps doing so.
13
14
  */
14
15
  export { pageLayoutPanelOpen, pageLayoutSheet, openPagePanel as openConversationPanel, } from '@datalayer/primer-addons/lib/reactor';
16
+ export { SplitLayout, SPLIT_DEFAULT_SHARE, SPLIT_KEY_STEP, SPLIT_MAX_SHARE, SPLIT_MIN_SHARE, clampSplitShare, splitShareForKey, } from './SplitLayout';
15
17
  export const LOOP_PAGE_LAYOUT_PLUGIN_NAME = '@datalayer/loop-plugin-page-layout';
16
18
  /* A signal to read when no chat contributed a turn: the hook needs one. */
17
19
  const NO_TURN = signal({ id: 0, status: 'idle' });
@@ -91,6 +93,7 @@ function ConversationToggle() {
91
93
  export const LoopPageLayoutPlugin = definePlugin({
92
94
  name: LOOP_PAGE_LAYOUT_PLUGIN_NAME,
93
95
  config: {
96
+ arrangement: 'page',
94
97
  turnPanel: 'below',
95
98
  turnPanelFooter: 'full',
96
99
  prompt: 'docked',
@@ -98,10 +101,16 @@ export const LoopPageLayoutPlugin = definePlugin({
98
101
  pageSize: undefined,
99
102
  },
100
103
  displayName: 'Page layout',
101
- description: 'The editor — or, in the chat view, the conversation — on a centred sheet, the prompt docked above it at the same width or floating over it as a draggable card, the current turn under the prompt, the conversation in a side panel.',
104
+ description: 'The editor — or, in the chat view, the conversation — on a centred sheet, the prompt docked above it at the same width or floating over it as a draggable card, the current turn under the prompt, the conversation in a side panel; or, split, the conversation and the work side by side with a hairline to drag.',
102
105
  octicon: 'file',
103
106
  emoji: '\u{1F4C4}',
104
107
  build: ({ config, ...ctx }) => {
108
+ if (config.arrangement === 'split') {
109
+ // The conversation is on screen: the composer docked under it, and
110
+ // nothing that stands in for a transcript out of sight.
111
+ ctx.contribute(LoopChatLayout, { id: 'split-layout', prompt: 'docked', Component: SplitLayout }, { id: 'split-layout' });
112
+ return { components: [] };
113
+ }
105
114
  /*
106
115
  The layout, contributed per build because the composer's stance is
107
116
  configuration: `docked-top` means the layout owns the composer's width
@@ -34,6 +34,11 @@ export type LoopPresetOptions = {
34
34
  * A surface id rather than a fixed union, because the surfaces are
35
35
  * contributed: an editor plugin the preset has never heard of is still a
36
36
  * valid answer, and `'none'` is how a host asks for the chat alone.
37
+ *
38
+ * The notebook when unsaid, and none when `editors` is off. Said with
39
+ * `editors` off, it is honoured: the surface of a plugin the host mounts —
40
+ * an application's page — opens beside the chat without the notebook and
41
+ * the document.
37
42
  */
38
43
  defaultEditor?: ChatPluginConfig['defaultSurface'];
39
44
  /** Whether the chat offers its surface switcher. */
@@ -82,6 +87,13 @@ export type LoopPresetOptions = {
82
87
  * `LoopChatLayout` point.
83
88
  */
84
89
  pageLayout?: boolean;
90
+ /**
91
+ * How the page layout arranges the parts: `page` (the default), the work
92
+ * on a sheet with the conversation in a panel; or `split`, the
93
+ * conversation and the work side by side with a hairline to drag. Only
94
+ * read with `pageLayout`.
95
+ */
96
+ pageLayoutArrangement?: 'page' | 'split';
85
97
  /**
86
98
  * Where the page layout hangs the current turn on the composer: `below`
87
99
  * (the default), `above`, or `none`. Only read with `pageLayout`.
@@ -52,12 +52,13 @@ import { LoopPageLayoutPlugin } from './plugins/page-layout';
52
52
  * be installed or signed into, and the switches a demonstration wants are off.
53
53
  */
54
54
  export function loopPlugins(options = {}) {
55
- const { serverUrl, target = 'browser', defaultEditor = 'notebook', showViewSelector = true, hideChatHeader = false, chatHeaderButtons = false, promptPlacement, autoFocusPrompt = true, suggestionLabels = true, fullScreenTopOffset = 0, showAgentVariants = false, agentSummary = true, teamId, teamPicker = true, pageLayout = false, pageLayoutTurnPanel = 'below', pageLayoutPrompt = 'docked', pageLayoutPromptAnchor = 'top', pageLayoutSize, pageLayoutTurnPanelFooter = 'full', localAgent, localAgentSpec, datalayerAgentSpecId, datalayerCreatePayload, targetFixed, themeVariant, presence, showTokenUsage, floatingPrompt = false, editorSelector = false, editors = true, graph = false, commandPalette = false, pluginsPanel = false, windowFrame = false, firstPromptHook, } = options;
55
+ const { serverUrl, target = 'browser', defaultEditor: askedEditor, showViewSelector = true, hideChatHeader = false, chatHeaderButtons = false, promptPlacement, autoFocusPrompt = true, suggestionLabels = true, fullScreenTopOffset = 0, showAgentVariants = false, agentSummary = true, teamId, teamPicker = true, pageLayout = false, pageLayoutArrangement = 'page', pageLayoutTurnPanel = 'below', pageLayoutPrompt = 'docked', pageLayoutPromptAnchor = 'top', pageLayoutSize, pageLayoutTurnPanelFooter = 'full', localAgent, localAgentSpec, datalayerAgentSpecId, datalayerCreatePayload, targetFixed, themeVariant, presence, showTokenUsage, floatingPrompt = false, editorSelector = false, editors = true, graph = false, commandPalette = false, pluginsPanel = false, windowFrame = false, firstPromptHook, } = options;
56
+ const defaultEditor = askedEditor ?? (editors ? 'notebook' : 'none');
56
57
  return [
57
58
  // The chat owns the editor beside it, so which one opens is its
58
59
  // configuration rather than the workspace's.
59
60
  configurePlugin(ChatPlugin, {
60
- defaultSurface: editors ? defaultEditor : 'none',
61
+ defaultSurface: defaultEditor,
61
62
  // The chat's strip stands down when the header selector offers the
62
63
  // same choice; see `editorSelector` — and when there is no editor
63
64
  // to choose.
@@ -130,13 +131,14 @@ export function loopPlugins(options = {}) {
130
131
  // happens to be on is a workspace with a trap in it. The selector itself
131
132
  // stays behind the old switch.
132
133
  configurePlugin(ShellPlugin, {
133
- defaultEditor: editors ? defaultEditor : 'none',
134
+ defaultEditor,
134
135
  showSelector: editors && editorSelector,
135
136
  }),
136
137
  ...(floatingPrompt ? [PromptPlugin] : []),
137
138
  ...(pageLayout
138
139
  ? [
139
140
  configurePlugin(LoopPageLayoutPlugin, {
141
+ arrangement: pageLayoutArrangement,
140
142
  turnPanel: pageLayoutTurnPanel,
141
143
  turnPanelFooter: pageLayoutTurnPanelFooter,
142
144
  prompt: pageLayoutPrompt,
@@ -439,6 +439,7 @@ export const SERVER_ACTIONS = {
439
439
  * to reproduce.
440
440
  */
441
441
  export const APP_BEHAVIOURS = {
442
+ 'customer-interview': {},
442
443
  'data-quality': {},
443
444
  'inbox-triage': {
444
445
  'google-workspace.append_table_rows': 'leave_to_me',
@@ -563,9 +564,12 @@ export const APP_BEHAVIOURS = {
563
564
  'google-workspace.update_paragraph_style': 'leave_to_me',
564
565
  },
565
566
  'model-choice': {},
567
+ 'pipeline-report': {},
566
568
  'quote-calculator': {},
569
+ 'report-from-a-file': {},
567
570
  'ship-or-fix': {},
568
571
  'supplier-comparison': {},
572
+ 'support-desk': {},
569
573
  'web-research': {
570
574
  'tavily.tavily_crawl': 'do_it',
571
575
  'tavily.tavily_extract': 'do_it',
@@ -579,6 +583,7 @@ export const APP_BEHAVIOURS = {
579
583
  * application and tool, each condition and the behaviour when it holds.
580
584
  */
581
585
  export const APP_ESCALATIONS = {
586
+ 'customer-interview': {},
582
587
  'data-quality': {},
583
588
  'inbox-triage': {
584
589
  'google-workspace.batch_modify_gmail_message_labels': [
@@ -615,8 +620,11 @@ export const APP_ESCALATIONS = {
615
620
  ],
616
621
  },
617
622
  'model-choice': {},
623
+ 'pipeline-report': {},
618
624
  'quote-calculator': {},
625
+ 'report-from-a-file': {},
619
626
  'ship-or-fix': {},
620
627
  'supplier-comparison': {},
628
+ 'support-desk': {},
621
629
  'web-research': {},
622
630
  };
@@ -3380,6 +3380,7 @@ export const EXAMPLE_ONE_TRIGGER_AGENTSPEC_0_0_1 = {
3380
3380
  domain: undefined,
3381
3381
  enabled: true,
3382
3382
  model: 'bedrock:us.anthropic.claude-sonnet-4-6',
3383
+ modelAdditionals: ['alibaba:qwen-max'],
3383
3384
  mcpServers: [],
3384
3385
  skills: [
3385
3386
  SKILL_MAP['github:0.0.1']
@@ -4389,6 +4390,7 @@ export const EXAMPLE_SIMPLE_AGENTSPEC_0_0_1 = {
4389
4390
  domain: undefined,
4390
4391
  enabled: true,
4391
4392
  model: 'bedrock:us.anthropic.claude-sonnet-4-6',
4393
+ modelAdditionals: ['alibaba:qwen-max'],
4392
4394
  mcpServers: [],
4393
4395
  skills: [
4394
4396
  SKILL_MAP['events:0.0.1']
@@ -5021,6 +5023,7 @@ export const JUPYTER_NOTEBOOK_COMPACTOR_AGENTSPEC_0_0_1 = {
5021
5023
  domain: undefined,
5022
5024
  enabled: true,
5023
5025
  model: 'bedrock:us.anthropic.claude-sonnet-4-6',
5026
+ modelAdditionals: ['alibaba:qwen-max'],
5024
5027
  mcpServers: [],
5025
5028
  skills: [].filter(Boolean),
5026
5029
  tools: [TOOL_MAP['runtime-echo:0.0.1']],
@@ -7663,6 +7666,7 @@ export const WORKER_CRAWLER_AGENTSPEC_0_0_1 = {
7663
7666
  domain: 'market-analyst',
7664
7667
  enabled: true,
7665
7668
  model: 'bedrock:us.anthropic.claude-sonnet-4-6',
7669
+ modelAdditionals: ['alibaba:qwen-max'],
7666
7670
  mcpServers: [MCP_SERVER_MAP['tavily:0.0.1']],
7667
7671
  skills: [
7668
7672
  SKILL_MAP['github:0.0.1']
@@ -7,13 +7,17 @@
7
7
  * This file is AUTO-GENERATED from YAML specifications.
8
8
  * DO NOT EDIT MANUALLY - run 'make specs' to regenerate.
9
9
  */
10
- import type { AppKind, AppSpec } from '../types/agentspecs';
10
+ import type { AppBuilt, AppKind, AppSpec } from '../types/agentspecs';
11
+ export declare const CUSTOMER_INTERVIEW_APP_0_0_1: AppSpec;
11
12
  export declare const DATA_QUALITY_APP_0_0_1: AppSpec;
12
13
  export declare const INBOX_TRIAGE_APP_0_0_1: AppSpec;
13
14
  export declare const MODEL_CHOICE_APP_0_0_1: AppSpec;
15
+ export declare const PIPELINE_REPORT_APP_0_0_1: AppSpec;
14
16
  export declare const QUOTE_CALCULATOR_APP_0_0_1: AppSpec;
17
+ export declare const REPORT_FROM_A_FILE_APP_0_0_1: AppSpec;
15
18
  export declare const SHIP_OR_FIX_APP_0_0_1: AppSpec;
16
19
  export declare const SUPPLIER_COMPARISON_APP_0_0_1: AppSpec;
20
+ export declare const SUPPORT_DESK_APP_0_0_1: AppSpec;
17
21
  export declare const WEB_RESEARCH_APP_0_0_1: AppSpec;
18
22
  export declare const APP_CATALOGUE: Record<string, AppSpec>;
19
23
  /** An application, by `id` or `id:version`, or undefined. */
@@ -24,5 +28,10 @@ export declare function getApp(ref: string): AppSpec | undefined;
24
28
  * Appspec have to give back.
25
29
  */
26
30
  export declare const APP_SOURCES: Record<string, Record<string, unknown>>;
31
+ /**
32
+ * How each application was built: `python` (its `app.py`), `canvas` (its
33
+ * page composed on the Canvas) or `written` (its spec written out).
34
+ */
35
+ export declare const APP_BUILT: Record<string, AppBuilt>;
27
36
  /** Every application of the catalogue, or those of a kind. */
28
37
  export declare function listApps(kind?: AppKind): AppSpec[];