@datalayer/agent-runtimes 1.3.17 → 1.3.19

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.
@@ -103,6 +103,8 @@ export function emptyAppspec(kind = 'chat') {
103
103
  tags: [],
104
104
  icon: DEFAULT_ICON,
105
105
  emoji: DEFAULT_EMOJI,
106
+ avatar: '',
107
+ banner: '',
106
108
  };
107
109
  }
108
110
  // --- reading --------------------------------------------------------------------------
@@ -139,6 +141,8 @@ const KNOWN_KEYS = [
139
141
  'tags',
140
142
  'icon',
141
143
  'emoji',
144
+ 'avatar',
145
+ 'banner',
142
146
  ];
143
147
  function parseConnection(data) {
144
148
  return {
@@ -353,6 +357,8 @@ export function parseAppspec(document) {
353
357
  tags: texts(data.tags),
354
358
  icon: text(data.icon, DEFAULT_ICON),
355
359
  emoji: text(data.emoji, DEFAULT_EMOJI) || DEFAULT_EMOJI,
360
+ avatar: text(data.avatar).trim(),
361
+ banner: text(data.banner).trim(),
356
362
  };
357
363
  if (isData(data.decision)) {
358
364
  app.decision = parseDecision(data.decision);
@@ -559,7 +565,9 @@ export function dumpAppspec(app) {
559
565
  .value('enabled', app.enabled, true)
560
566
  .list('tags', app.tags)
561
567
  .text('icon', app.icon ?? DEFAULT_ICON, DEFAULT_ICON)
562
- .text('emoji', app.emoji, DEFAULT_EMOJI);
568
+ .text('emoji', app.emoji, DEFAULT_EMOJI)
569
+ .text('avatar', app.avatar ?? '', '')
570
+ .text('banner', app.banner ?? '', '');
563
571
  return writer.data;
564
572
  }
565
573
  /** The keys of a document in the order they are written. */
@@ -408,9 +408,18 @@ export function documentShapeProblems(document) {
408
408
  'memory',
409
409
  'emoji',
410
410
  'icon',
411
+ 'avatar',
412
+ 'banner',
411
413
  ]) {
412
414
  text(d[key], key);
413
415
  }
416
+ // A drawing is named as it is in the profile's sets, `AstronautIcon`.
417
+ for (const key of ['avatar', 'banner']) {
418
+ const name = typeof d[key] === 'string' ? d[key].trim() : '';
419
+ if (name && !/^[A-Z][A-Za-z0-9]{0,63}$/.test(name)) {
420
+ at(key, 'is named as its drawing is, `AstronautIcon`');
421
+ }
422
+ }
414
423
  for (const key of [
415
424
  'skills',
416
425
  'tools',
package/lib/specs/apps.js CHANGED
@@ -154,6 +154,8 @@ export const INBOX_TRIAGE_APP_0_0_1 = {
154
154
  tags: ['example', 'worker', 'mail'],
155
155
  icon: 'mail',
156
156
  emoji: '📬',
157
+ avatar: '',
158
+ banner: '',
157
159
  setup: [
158
160
  "The agent 'worker-mail-triage:0.0.1' is not enabled.",
159
161
  "The MCP server 'google-workspace:0.0.1' is not enabled.",
@@ -342,6 +344,8 @@ export const QUOTE_CALCULATOR_APP_0_0_1 = {
342
344
  tags: ['example', 'widget'],
343
345
  icon: 'number',
344
346
  emoji: '🧮',
347
+ avatar: '',
348
+ banner: '',
345
349
  setup: ["The agent 'jupyter-data-analyst:0.0.1' is not enabled."],
346
350
  };
347
351
  export const SHIP_OR_FIX_APP_0_0_1 = {
@@ -497,6 +501,8 @@ export const SHIP_OR_FIX_APP_0_0_1 = {
497
501
  tags: ['example', 'decision', 'benchmarks'],
498
502
  icon: 'checklist',
499
503
  emoji: '🚢',
504
+ avatar: '',
505
+ banner: '',
500
506
  setup: ["The agent 'jupyter-data-analyst:0.0.1' is not enabled."],
501
507
  };
502
508
  export const WEB_RESEARCH_APP_0_0_1 = {
@@ -603,6 +609,8 @@ export const WEB_RESEARCH_APP_0_0_1 = {
603
609
  tags: ['example', 'research'],
604
610
  icon: 'search',
605
611
  emoji: '🔎',
612
+ avatar: '',
613
+ banner: '',
606
614
  setup: [],
607
615
  };
608
616
  export const APP_CATALOGUE = {
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The Appspec's JSON Schema, generated from agentspecs by
3
+ * `scripts/codegen/generate_apps.py`. Do not edit.
4
+ *
5
+ * @module specs/appspecSchema
6
+ */
7
+ export type JsonSchema = {
8
+ [key: string]: unknown;
9
+ type?: string;
10
+ description?: string;
11
+ properties?: Record<string, JsonSchema>;
12
+ items?: JsonSchema;
13
+ enum?: readonly unknown[];
14
+ anyOf?: readonly JsonSchema[];
15
+ $ref?: string;
16
+ $defs?: Record<string, JsonSchema>;
17
+ };
18
+ export declare const APPSPEC_SCHEMA: JsonSchema;
@@ -0,0 +1,982 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ export const APPSPEC_SCHEMA = {
6
+ $defs: {
7
+ Accent: {
8
+ description: 'The one colour of an application; everything else is neutral.',
9
+ enum: ['green', 'rose', 'sky', 'lime', 'sun', 'violet'],
10
+ title: 'Accent',
11
+ type: 'string',
12
+ },
13
+ Access: {
14
+ description: 'How far a connection goes.',
15
+ enum: ['read', 'write'],
16
+ title: 'Access',
17
+ type: 'string',
18
+ },
19
+ ActsAs: {
20
+ description: 'In whose name a connection acts.',
21
+ enum: ['owner', 'user'],
22
+ title: 'ActsAs',
23
+ type: 'string',
24
+ },
25
+ AppChecks: {
26
+ additionalProperties: false,
27
+ description: 'Optional: checks from the catalogue, for a builder who wants them.',
28
+ properties: {
29
+ guards: {
30
+ description: 'Guards, `id` or `id:version`',
31
+ items: {
32
+ type: 'string',
33
+ },
34
+ title: 'Guards',
35
+ type: 'array',
36
+ },
37
+ gates: {
38
+ description: 'Gates, `id` or `id:version`',
39
+ items: {
40
+ type: 'string',
41
+ },
42
+ title: 'Gates',
43
+ type: 'array',
44
+ },
45
+ track: {
46
+ default: '',
47
+ description: 'A Track, `id` or `id:version`',
48
+ title: 'Track',
49
+ type: 'string',
50
+ },
51
+ },
52
+ title: 'AppChecks',
53
+ type: 'object',
54
+ },
55
+ AppComputer: {
56
+ additionalProperties: false,
57
+ description: 'What the application may do on its own computer. Each is off until it is turned on.',
58
+ properties: {
59
+ browse: {
60
+ default: false,
61
+ description: 'Open pages in a browser',
62
+ title: 'Browse',
63
+ type: 'boolean',
64
+ },
65
+ files: {
66
+ default: false,
67
+ description: 'Read and write files',
68
+ title: 'Files',
69
+ type: 'boolean',
70
+ },
71
+ shell: {
72
+ default: false,
73
+ description: 'Run commands',
74
+ title: 'Shell',
75
+ type: 'boolean',
76
+ },
77
+ },
78
+ title: 'AppComputer',
79
+ type: 'object',
80
+ },
81
+ AppConnection: {
82
+ additionalProperties: false,
83
+ description: 'Something the application reaches.',
84
+ properties: {
85
+ server: {
86
+ description: 'An MCP server of the catalogue, `id` or `id:version`',
87
+ title: 'Server',
88
+ type: 'string',
89
+ },
90
+ access: {
91
+ $ref: '#/$defs/Access',
92
+ default: 'read',
93
+ description: 'How far: `read`, or `write`',
94
+ },
95
+ as: {
96
+ $ref: '#/$defs/ActsAs',
97
+ default: 'owner',
98
+ description: "In whose name: `owner` (the builder's account) or `user` (each user's own)",
99
+ },
100
+ only: {
101
+ description: 'The tools of the server the application may use, by name or pattern (`*gmail*`: `*` is any run of characters, `?` any one); all of them when empty. A tool left out is not reached at all',
102
+ items: {
103
+ type: 'string',
104
+ },
105
+ title: 'Only',
106
+ type: 'array',
107
+ },
108
+ },
109
+ required: ['server'],
110
+ title: 'AppConnection',
111
+ type: 'object',
112
+ },
113
+ AppCriterion: {
114
+ additionalProperties: false,
115
+ description: 'What an alternative is judged on.',
116
+ properties: {
117
+ name: {
118
+ description: 'Its name',
119
+ title: 'Name',
120
+ type: 'string',
121
+ },
122
+ kind: {
123
+ $ref: '#/$defs/CriterionKind',
124
+ default: 'metric',
125
+ description: '`metric`, `noul`, `choice`, `score`',
126
+ },
127
+ weight: {
128
+ default: 1,
129
+ description: 'How much it counts',
130
+ minimum: 0,
131
+ title: 'Weight',
132
+ type: 'number',
133
+ },
134
+ instructions: {
135
+ default: '',
136
+ description: 'What a judgment model is asked, or how a metric is computed',
137
+ title: 'Instructions',
138
+ type: 'string',
139
+ },
140
+ options: {
141
+ description: 'For a choice or a score: from the worst to the best',
142
+ items: {
143
+ type: 'string',
144
+ },
145
+ title: 'Options',
146
+ type: 'array',
147
+ },
148
+ direction: {
149
+ default: 'higher',
150
+ description: 'Whether more counts for, or against',
151
+ pattern: '^(higher|lower)$',
152
+ title: 'Direction',
153
+ type: 'string',
154
+ },
155
+ measure: {
156
+ default: '',
157
+ description: 'For a metric: what a benchmark run fills it from',
158
+ pattern: '^(|pass_rate|cost_per_task|seconds_per_task)$',
159
+ title: 'Measure',
160
+ type: 'string',
161
+ },
162
+ },
163
+ required: ['name'],
164
+ title: 'AppCriterion',
165
+ type: 'object',
166
+ },
167
+ AppDecision: {
168
+ additionalProperties: false,
169
+ description: 'What a decision application decides.',
170
+ properties: {
171
+ question: {
172
+ description: 'The question it answers',
173
+ title: 'Question',
174
+ type: 'string',
175
+ },
176
+ alternatives: {
177
+ description: 'What is chosen between',
178
+ items: {
179
+ type: 'string',
180
+ },
181
+ title: 'Alternatives',
182
+ type: 'array',
183
+ },
184
+ criteria: {
185
+ description: 'What each is judged on',
186
+ items: {
187
+ $ref: '#/$defs/AppCriterion',
188
+ },
189
+ title: 'Criteria',
190
+ type: 'array',
191
+ },
192
+ min_confidence: {
193
+ default: 0,
194
+ description: 'A judgment less confident than this is put to the reader',
195
+ maximum: 1,
196
+ minimum: 0,
197
+ title: 'Min Confidence',
198
+ type: 'number',
199
+ },
200
+ scenarios: {
201
+ description: 'Named sets of weights',
202
+ items: {
203
+ $ref: '#/$defs/AppScenario',
204
+ },
205
+ title: 'Scenarios',
206
+ type: 'array',
207
+ },
208
+ judgment_model: {
209
+ default: '',
210
+ description: 'The model that answers the judgments',
211
+ title: 'Judgment Model',
212
+ type: 'string',
213
+ },
214
+ },
215
+ required: ['question'],
216
+ title: 'AppDecision',
217
+ type: 'object',
218
+ },
219
+ AppDeployment: {
220
+ additionalProperties: false,
221
+ description: 'Where the application goes.',
222
+ properties: {
223
+ hosted: {
224
+ anyOf: [
225
+ {
226
+ $ref: '#/$defs/HostedDeployment',
227
+ },
228
+ {
229
+ type: 'null',
230
+ },
231
+ ],
232
+ default: null,
233
+ description: 'At an address of its own',
234
+ },
235
+ embedded: {
236
+ anyOf: [
237
+ {
238
+ $ref: '#/$defs/EmbeddedDeployment',
239
+ },
240
+ {
241
+ type: 'null',
242
+ },
243
+ ],
244
+ default: null,
245
+ description: 'Inside another product',
246
+ },
247
+ },
248
+ title: 'AppDeployment',
249
+ type: 'object',
250
+ },
251
+ AppInterface: {
252
+ additionalProperties: false,
253
+ description: 'What the user sees.',
254
+ properties: {
255
+ layout: {
256
+ anyOf: [
257
+ {
258
+ $ref: '#/$defs/Layout',
259
+ },
260
+ {
261
+ type: 'null',
262
+ },
263
+ ],
264
+ default: null,
265
+ description: "`chat`, `page` or `split`; the kind's own when unsaid",
266
+ },
267
+ accent: {
268
+ $ref: '#/$defs/Accent',
269
+ default: 'green',
270
+ description: "The application's one colour",
271
+ },
272
+ welcome: {
273
+ default: '',
274
+ description: 'What the application says first',
275
+ title: 'Welcome',
276
+ type: 'string',
277
+ },
278
+ starters: {
279
+ description: 'First messages offered to the user',
280
+ items: {
281
+ $ref: '#/$defs/AppStarter',
282
+ },
283
+ title: 'Starters',
284
+ type: 'array',
285
+ },
286
+ settings: {
287
+ description: 'What the user may set',
288
+ items: {
289
+ $ref: '#/$defs/AppSetting',
290
+ },
291
+ title: 'Settings',
292
+ type: 'array',
293
+ },
294
+ components: {
295
+ description: "The components of the catalog the surface may use; the kind's own when empty",
296
+ items: {
297
+ type: 'string',
298
+ },
299
+ title: 'Components',
300
+ type: 'array',
301
+ },
302
+ surface: {
303
+ anyOf: [
304
+ {
305
+ $ref: '#/$defs/AppSurface',
306
+ },
307
+ {
308
+ type: 'null',
309
+ },
310
+ ],
311
+ default: null,
312
+ description: 'The component tree, when there is one',
313
+ },
314
+ },
315
+ title: 'AppInterface',
316
+ type: 'object',
317
+ },
318
+ AppKind: {
319
+ description: 'What kind of application it is: what its user meets.',
320
+ enum: ['chat', 'widget', 'decision', 'worker'],
321
+ title: 'AppKind',
322
+ type: 'string',
323
+ },
324
+ AppPermissions: {
325
+ additionalProperties: false,
326
+ description: 'What the application may reach beside its connections. Nothing, unless said.',
327
+ properties: {
328
+ spaces: {
329
+ description: 'The Spaces it reads or writes',
330
+ items: {
331
+ $ref: '#/$defs/AppSpaceGrant',
332
+ },
333
+ title: 'Spaces',
334
+ type: 'array',
335
+ },
336
+ computer: {
337
+ $ref: '#/$defs/AppComputer',
338
+ description: 'Its computer: browse, files, shell',
339
+ },
340
+ },
341
+ title: 'AppPermissions',
342
+ type: 'object',
343
+ },
344
+ AppRecord: {
345
+ additionalProperties: false,
346
+ description: 'What is kept of what the application did, and for how long.',
347
+ properties: {
348
+ keep_for: {
349
+ default: '1_years',
350
+ description: 'How long: `90_days`, `18_months`, `1_years`',
351
+ title: 'Keep For',
352
+ type: 'string',
353
+ },
354
+ include: {
355
+ description: 'What is kept',
356
+ items: {
357
+ $ref: '#/$defs/RecordItem',
358
+ },
359
+ title: 'Include',
360
+ type: 'array',
361
+ },
362
+ },
363
+ title: 'AppRecord',
364
+ type: 'object',
365
+ },
366
+ AppRule: {
367
+ additionalProperties: false,
368
+ description: 'When the application acts alone, and when it asks.',
369
+ properties: {
370
+ action: {
371
+ description: 'The action, in the words a person reads: `Send an email`',
372
+ title: 'Action',
373
+ type: 'string',
374
+ },
375
+ applies_to: {
376
+ anyOf: [
377
+ {
378
+ type: 'string',
379
+ },
380
+ {
381
+ items: {
382
+ type: 'string',
383
+ },
384
+ type: 'array',
385
+ },
386
+ ],
387
+ description: 'What the rule applies to: a class of action (`read`, `write`, `send`, `buy`, `delete`, `publish`), or named tools (`server.tool`, or a tool id)',
388
+ title: 'Applies To',
389
+ },
390
+ behaviour: {
391
+ $ref: '#/$defs/Behaviour',
392
+ description: '`do_it`, `if_asked`, `ask_first` or `leave_to_me`',
393
+ },
394
+ },
395
+ required: ['action', 'applies_to', 'behaviour'],
396
+ title: 'AppRule',
397
+ type: 'object',
398
+ },
399
+ AppScenario: {
400
+ additionalProperties: false,
401
+ description: 'A named set of weights: one way of looking at the same findings.',
402
+ properties: {
403
+ name: {
404
+ description: 'Its name',
405
+ title: 'Name',
406
+ type: 'string',
407
+ },
408
+ weights: {
409
+ additionalProperties: {
410
+ type: 'number',
411
+ },
412
+ description: 'By criterion name',
413
+ title: 'Weights',
414
+ type: 'object',
415
+ },
416
+ },
417
+ required: ['name'],
418
+ title: 'AppScenario',
419
+ type: 'object',
420
+ },
421
+ AppSetting: {
422
+ additionalProperties: false,
423
+ description: 'Something the user may set for their session.',
424
+ properties: {
425
+ id: {
426
+ description: 'The name the application reads it by',
427
+ title: 'Id',
428
+ type: 'string',
429
+ },
430
+ type: {
431
+ $ref: '#/$defs/SettingType',
432
+ description: '`select`, `text`, `toggle`, `slider` or `number`',
433
+ },
434
+ label: {
435
+ description: 'What the user reads',
436
+ title: 'Label',
437
+ type: 'string',
438
+ },
439
+ options: {
440
+ description: 'For a select: its options',
441
+ items: {
442
+ type: 'string',
443
+ },
444
+ title: 'Options',
445
+ type: 'array',
446
+ },
447
+ default: {
448
+ anyOf: [
449
+ {
450
+ type: 'string',
451
+ },
452
+ {
453
+ type: 'boolean',
454
+ },
455
+ {
456
+ type: 'number',
457
+ },
458
+ {
459
+ type: 'null',
460
+ },
461
+ ],
462
+ default: null,
463
+ description: 'Its value at the start',
464
+ title: 'Default',
465
+ },
466
+ min: {
467
+ anyOf: [
468
+ {
469
+ type: 'number',
470
+ },
471
+ {
472
+ type: 'null',
473
+ },
474
+ ],
475
+ default: null,
476
+ description: 'For a slider or a number: the least',
477
+ title: 'Min',
478
+ },
479
+ max: {
480
+ anyOf: [
481
+ {
482
+ type: 'number',
483
+ },
484
+ {
485
+ type: 'null',
486
+ },
487
+ ],
488
+ default: null,
489
+ description: 'For a slider or a number: the most',
490
+ title: 'Max',
491
+ },
492
+ },
493
+ required: ['id', 'type', 'label'],
494
+ title: 'AppSetting',
495
+ type: 'object',
496
+ },
497
+ AppSpaceGrant: {
498
+ additionalProperties: false,
499
+ description: 'A Space the application may reach.',
500
+ properties: {
501
+ space: {
502
+ description: 'The Space, by its handle or its id',
503
+ title: 'Space',
504
+ type: 'string',
505
+ },
506
+ access: {
507
+ $ref: '#/$defs/Access',
508
+ default: 'read',
509
+ description: '`read`, or `write`',
510
+ },
511
+ },
512
+ required: ['space'],
513
+ title: 'AppSpaceGrant',
514
+ type: 'object',
515
+ },
516
+ AppStarter: {
517
+ additionalProperties: false,
518
+ description: 'A first message offered to the user.',
519
+ properties: {
520
+ label: {
521
+ description: 'What the button says',
522
+ title: 'Label',
523
+ type: 'string',
524
+ },
525
+ message: {
526
+ description: 'What is sent when it is chosen',
527
+ title: 'Message',
528
+ type: 'string',
529
+ },
530
+ },
531
+ required: ['label', 'message'],
532
+ title: 'AppStarter',
533
+ type: 'object',
534
+ },
535
+ AppSurface: {
536
+ additionalProperties: false,
537
+ description: 'The component tree the user meets, over the approved catalog (A2UI).',
538
+ properties: {
539
+ protocol: {
540
+ default: 'a2ui/v0.9',
541
+ description: 'The protocol the tree is written in',
542
+ title: 'Protocol',
543
+ type: 'string',
544
+ },
545
+ components: {
546
+ description: "The components, as the protocol's `updateComponents` carries them",
547
+ items: {
548
+ additionalProperties: true,
549
+ type: 'object',
550
+ },
551
+ title: 'Components',
552
+ type: 'array',
553
+ },
554
+ composed_by: {
555
+ default: '',
556
+ description: "Who composed it: a model's id, `developer`, `template`",
557
+ title: 'Composed By',
558
+ type: 'string',
559
+ },
560
+ composed_at: {
561
+ default: '',
562
+ description: 'When, as an ISO date',
563
+ title: 'Composed At',
564
+ type: 'string',
565
+ },
566
+ },
567
+ title: 'AppSurface',
568
+ type: 'object',
569
+ },
570
+ AppTestCase: {
571
+ additionalProperties: false,
572
+ description: 'An example of what the application should do, in plain words.',
573
+ properties: {
574
+ ask: {
575
+ description: 'What it is asked',
576
+ title: 'Ask',
577
+ type: 'string',
578
+ },
579
+ expect: {
580
+ description: 'What it should do',
581
+ title: 'Expect',
582
+ type: 'string',
583
+ },
584
+ },
585
+ required: ['ask', 'expect'],
586
+ title: 'AppTestCase',
587
+ type: 'object',
588
+ },
589
+ AppTests: {
590
+ additionalProperties: false,
591
+ description: 'How the application is verified.',
592
+ properties: {
593
+ ready_at: {
594
+ default: 0.8,
595
+ description: 'The share of tests that has to pass for the application to be ready',
596
+ maximum: 1,
597
+ minimum: 0,
598
+ title: 'Ready At',
599
+ type: 'number',
600
+ },
601
+ evalset: {
602
+ default: '',
603
+ description: 'An evalset its runs validate against, when one is chosen',
604
+ title: 'Evalset',
605
+ type: 'string',
606
+ },
607
+ cases: {
608
+ description: 'Its test conversations',
609
+ items: {
610
+ $ref: '#/$defs/AppTestCase',
611
+ },
612
+ title: 'Cases',
613
+ type: 'array',
614
+ },
615
+ },
616
+ title: 'AppTests',
617
+ type: 'object',
618
+ },
619
+ AppTrigger: {
620
+ additionalProperties: false,
621
+ description: "What starts a worker's work.",
622
+ properties: {
623
+ type: {
624
+ $ref: '#/$defs/TriggerType',
625
+ description: '`schedule`, `event` or `once`',
626
+ },
627
+ cron: {
628
+ default: '',
629
+ description: 'For a schedule: a cron expression, `0 8 * * *`',
630
+ title: 'Cron',
631
+ type: 'string',
632
+ },
633
+ event: {
634
+ default: '',
635
+ description: 'For an event: its name, `email_received`',
636
+ title: 'Event',
637
+ type: 'string',
638
+ },
639
+ at: {
640
+ default: '',
641
+ description: 'For once: when, as an ISO date',
642
+ title: 'At',
643
+ type: 'string',
644
+ },
645
+ description: {
646
+ default: '',
647
+ description: 'What it is, in words: `Every morning at 8`',
648
+ title: 'Description',
649
+ type: 'string',
650
+ },
651
+ prompt: {
652
+ default: '',
653
+ description: 'What the worker is told when it fires',
654
+ title: 'Prompt',
655
+ type: 'string',
656
+ },
657
+ },
658
+ required: ['type'],
659
+ title: 'AppTrigger',
660
+ type: 'object',
661
+ },
662
+ Behaviour: {
663
+ description: 'What an application does when it meets an action: the four a person chooses from.',
664
+ enum: ['do_it', 'if_asked', 'ask_first', 'leave_to_me'],
665
+ title: 'Behaviour',
666
+ type: 'string',
667
+ },
668
+ CriterionKind: {
669
+ description: 'How a criterion is assessed.',
670
+ enum: ['metric', 'noul', 'choice', 'score'],
671
+ title: 'CriterionKind',
672
+ type: 'string',
673
+ },
674
+ EmbedMode: {
675
+ description: "How an application sits in another product's page.",
676
+ enum: ['inline', 'bubble', 'panel'],
677
+ title: 'EmbedMode',
678
+ type: 'string',
679
+ },
680
+ EmbeddedDeployment: {
681
+ additionalProperties: false,
682
+ description: "The application inside another product's page.",
683
+ properties: {
684
+ mode: {
685
+ $ref: '#/$defs/EmbedMode',
686
+ default: 'inline',
687
+ description: '`inline`, `bubble` or `panel`',
688
+ },
689
+ origins: {
690
+ description: 'The origins allowed to embed it',
691
+ items: {
692
+ type: 'string',
693
+ },
694
+ title: 'Origins',
695
+ type: 'array',
696
+ },
697
+ },
698
+ title: 'EmbeddedDeployment',
699
+ type: 'object',
700
+ },
701
+ HostedDeployment: {
702
+ additionalProperties: false,
703
+ description: 'The application at an address of its own.',
704
+ properties: {
705
+ visibility: {
706
+ $ref: '#/$defs/Visibility',
707
+ default: 'private',
708
+ description: 'Who can open it',
709
+ },
710
+ slug: {
711
+ default: '',
712
+ description: 'The readable part of its address',
713
+ title: 'Slug',
714
+ type: 'string',
715
+ },
716
+ },
717
+ title: 'HostedDeployment',
718
+ type: 'object',
719
+ },
720
+ Layout: {
721
+ description: 'How the application is laid out.',
722
+ enum: ['chat', 'page', 'split'],
723
+ title: 'Layout',
724
+ type: 'string',
725
+ },
726
+ RecordItem: {
727
+ description: 'What an application may keep of what it did.',
728
+ enum: [
729
+ 'conversations',
730
+ 'actions',
731
+ 'decisions',
732
+ 'approvals',
733
+ 'checks',
734
+ 'sources',
735
+ 'outputs',
736
+ 'feedback',
737
+ ],
738
+ title: 'RecordItem',
739
+ type: 'string',
740
+ },
741
+ SettingType: {
742
+ description: 'What a setting is set with.',
743
+ enum: ['select', 'text', 'toggle', 'slider', 'number'],
744
+ title: 'SettingType',
745
+ type: 'string',
746
+ },
747
+ TriggerType: {
748
+ description: 'What starts a worker: the trigger kinds of the catalogue.',
749
+ enum: ['schedule', 'event', 'once'],
750
+ title: 'TriggerType',
751
+ type: 'string',
752
+ },
753
+ Visibility: {
754
+ description: 'Who can open a hosted application.',
755
+ enum: ['private', 'invited', 'organization', 'link', 'public'],
756
+ title: 'Visibility',
757
+ type: 'string',
758
+ },
759
+ },
760
+ additionalProperties: false,
761
+ description: 'Specification for an application.',
762
+ properties: {
763
+ schema: {
764
+ description: 'The version of the spec itself',
765
+ title: 'Schema',
766
+ const: 'loop.app/v1',
767
+ default: 'loop.app/v1',
768
+ },
769
+ id: {
770
+ description: 'Unique application identifier',
771
+ title: 'Id',
772
+ type: 'string',
773
+ },
774
+ version: {
775
+ default: '0.0.1',
776
+ description: 'Application version',
777
+ title: 'Version',
778
+ type: 'string',
779
+ },
780
+ name: {
781
+ description: 'Display name',
782
+ title: 'Name',
783
+ type: 'string',
784
+ },
785
+ kind: {
786
+ $ref: '#/$defs/AppKind',
787
+ description: '`chat`, `widget`, `decision` or `worker`',
788
+ },
789
+ description: {
790
+ default: '',
791
+ description: 'What it does, in a sentence',
792
+ title: 'Description',
793
+ type: 'string',
794
+ },
795
+ owner: {
796
+ default: '',
797
+ description: 'Who answers for it',
798
+ title: 'Owner',
799
+ type: 'string',
800
+ },
801
+ agent: {
802
+ default: '',
803
+ description: 'The agent or the Cog that does the work, `id` or `id:version`',
804
+ title: 'Agent',
805
+ type: 'string',
806
+ },
807
+ team: {
808
+ default: '',
809
+ description: 'Or a team of them, `id` or `id:version`',
810
+ title: 'Team',
811
+ type: 'string',
812
+ },
813
+ instructions: {
814
+ default: '',
815
+ description: 'What this application tells its agent, on top of its own',
816
+ title: 'Instructions',
817
+ type: 'string',
818
+ },
819
+ model: {
820
+ default: '',
821
+ description: "The model, when it is not the organization's default",
822
+ title: 'Model',
823
+ type: 'string',
824
+ },
825
+ skills: {
826
+ description: "Skills it adds to its agent's",
827
+ items: {
828
+ type: 'string',
829
+ },
830
+ title: 'Skills',
831
+ type: 'array',
832
+ },
833
+ tools: {
834
+ description: "Tools of the catalogue it adds to its agent's",
835
+ items: {
836
+ type: 'string',
837
+ },
838
+ title: 'Tools',
839
+ type: 'array',
840
+ },
841
+ context: {
842
+ description: 'The Frames it works under',
843
+ items: {
844
+ type: 'string',
845
+ },
846
+ title: 'Context',
847
+ type: 'array',
848
+ },
849
+ contents: {
850
+ description: 'The documents and datasets it answers from',
851
+ items: {
852
+ type: 'string',
853
+ },
854
+ title: 'Contents',
855
+ type: 'array',
856
+ },
857
+ connections: {
858
+ description: 'What it reaches',
859
+ items: {
860
+ $ref: '#/$defs/AppConnection',
861
+ },
862
+ title: 'Connections',
863
+ type: 'array',
864
+ },
865
+ rules: {
866
+ description: 'When it acts alone, and when it asks',
867
+ items: {
868
+ $ref: '#/$defs/AppRule',
869
+ },
870
+ title: 'Rules',
871
+ type: 'array',
872
+ },
873
+ permissions: {
874
+ $ref: '#/$defs/AppPermissions',
875
+ description: 'What else it may reach: Spaces, its computer. Nothing, unless said',
876
+ },
877
+ interface: {
878
+ $ref: '#/$defs/AppInterface',
879
+ description: 'What the user sees',
880
+ },
881
+ tests: {
882
+ $ref: '#/$defs/AppTests',
883
+ description: 'How it is verified',
884
+ },
885
+ record: {
886
+ $ref: '#/$defs/AppRecord',
887
+ description: 'What is kept of what it did',
888
+ },
889
+ checks: {
890
+ $ref: '#/$defs/AppChecks',
891
+ description: 'Optional checks from the catalogue',
892
+ },
893
+ deployment: {
894
+ $ref: '#/$defs/AppDeployment',
895
+ description: 'Where it goes',
896
+ },
897
+ goal: {
898
+ default: '',
899
+ description: 'For a worker: what it works toward',
900
+ title: 'Goal',
901
+ type: 'string',
902
+ },
903
+ triggers: {
904
+ description: 'For a worker: what starts its work',
905
+ items: {
906
+ $ref: '#/$defs/AppTrigger',
907
+ },
908
+ title: 'Triggers',
909
+ type: 'array',
910
+ },
911
+ memory: {
912
+ default: '',
913
+ description: 'A memory of the catalogue, when it remembers',
914
+ title: 'Memory',
915
+ type: 'string',
916
+ },
917
+ notifications: {
918
+ description: 'Where an approval reaches a person',
919
+ items: {
920
+ type: 'string',
921
+ },
922
+ title: 'Notifications',
923
+ type: 'array',
924
+ },
925
+ decision: {
926
+ anyOf: [
927
+ {
928
+ $ref: '#/$defs/AppDecision',
929
+ },
930
+ {
931
+ type: 'null',
932
+ },
933
+ ],
934
+ default: null,
935
+ description: 'For a decision: what it decides',
936
+ },
937
+ enabled: {
938
+ default: true,
939
+ description: 'Whether it is offered today',
940
+ title: 'Enabled',
941
+ type: 'boolean',
942
+ },
943
+ tags: {
944
+ items: {
945
+ type: 'string',
946
+ },
947
+ title: 'Tags',
948
+ type: 'array',
949
+ },
950
+ icon: {
951
+ default: 'apps',
952
+ description: 'Icon identifier',
953
+ title: 'Icon',
954
+ type: 'string',
955
+ },
956
+ emoji: {
957
+ default: '👀',
958
+ description: 'Its face: one emoji, shown wherever the application appears',
959
+ title: 'Emoji',
960
+ type: 'string',
961
+ },
962
+ avatar: {
963
+ default: '',
964
+ description: 'Its avatar, by name: a drawing of the set people choose theirs from on their profile. Its emoji stands for it when unsaid, and where only text goes',
965
+ pattern: '^\\s*([A-Z][A-Za-z0-9]{0,63})?\\s*$',
966
+ title: 'Avatar',
967
+ type: 'string',
968
+ },
969
+ banner: {
970
+ default: '',
971
+ description: 'Its banner, by name, from the set people choose theirs from on their profile. The one its id seeds when unsaid',
972
+ pattern: '^\\s*([A-Z][A-Za-z0-9]{0,63})?\\s*$',
973
+ title: 'Banner',
974
+ type: 'string',
975
+ },
976
+ },
977
+ required: ['id', 'name', 'kind'],
978
+ title: 'Appspec',
979
+ type: 'object',
980
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
981
+ $id: 'https://agentspecs.datalayer.tech/schemas/loop.app/v1.json',
982
+ };
@@ -27,3 +27,4 @@ export * from './tracks';
27
27
  export * from './ops';
28
28
  export * from './apps';
29
29
  export * from './actions';
30
+ export * from './appspecSchema';
@@ -31,3 +31,4 @@ export * from './tracks';
31
31
  export * from './ops';
32
32
  export * from './apps';
33
33
  export * from './actions';
34
+ export * from './appspecSchema';
@@ -524,6 +524,13 @@ export interface AppSpec {
524
524
  icon?: string;
525
525
  /** Its face: one emoji, shown wherever the application appears. */
526
526
  emoji: string;
527
+ /**
528
+ * Its avatar, by name: a drawing of the set people choose theirs from on
529
+ * their profile. Empty, its emoji stands for it.
530
+ */
531
+ avatar: string;
532
+ /** Its banner, by name, from the same profile's set. Empty, its id seeds one. */
533
+ banner: string;
527
534
  }
528
535
  export interface Agentspec {
529
536
  /** Unique agent identifier */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalayer/agent-runtimes",
3
- "version": "1.3.17",
3
+ "version": "1.3.19",
4
4
  "type": "module",
5
5
  "workspaces": [
6
6
  ".",
@@ -415,6 +415,49 @@ def _drop_nulls(value: Any) -> Any:
415
415
  return value
416
416
 
417
417
 
418
+ def generate_schema_typescript_code() -> str:
419
+ """The Appspec's JSON Schema, for the editors of the page (LOOP S-01, S-02).
420
+
421
+ Taken from the agentspecs the catalogue is generated from, so that what
422
+ an editor completes and explains is what the spec accepts.
423
+ """
424
+ from agentspecs.apps import json_schema
425
+
426
+ schema = json.dumps(json_schema(), indent=2, ensure_ascii=False, sort_keys=False)
427
+ return "\n".join(
428
+ [
429
+ "/*",
430
+ " * Copyright (c) 2025-2026 Datalayer, Inc.",
431
+ " * Distributed under the terms of the Modified BSD License.",
432
+ " */",
433
+ "",
434
+ "/**",
435
+ " * The Appspec's JSON Schema, generated from agentspecs by",
436
+ " * `scripts/codegen/generate_apps.py`. Do not edit.",
437
+ " *",
438
+ " * @module specs/appspecSchema",
439
+ " */",
440
+ "",
441
+ "/* eslint-disable */",
442
+ "",
443
+ "export type JsonSchema = {",
444
+ " [key: string]: unknown;",
445
+ " type?: string;",
446
+ " description?: string;",
447
+ " properties?: Record<string, JsonSchema>;",
448
+ " items?: JsonSchema;",
449
+ " enum?: readonly unknown[];",
450
+ " anyOf?: readonly JsonSchema[];",
451
+ " $ref?: string;",
452
+ " $defs?: Record<string, JsonSchema>;",
453
+ "};",
454
+ "",
455
+ f"export const APPSPEC_SCHEMA: JsonSchema = {schema};",
456
+ "",
457
+ ]
458
+ )
459
+
460
+
418
461
  def main() -> None:
419
462
  """Generate the application catalogue and the action classes."""
420
463
  parser = argparse.ArgumentParser(
@@ -439,6 +482,10 @@ def main() -> None:
439
482
  ),
440
483
  (args.actions_python_output, generate_actions_python_code(actions)),
441
484
  (args.actions_typescript_output, generate_actions_typescript_code(actions)),
485
+ (
486
+ args.typescript_output.with_name("appspecSchema.ts"),
487
+ generate_schema_typescript_code(),
488
+ ),
442
489
  ]
443
490
  for path, text in outputs:
444
491
  path.parent.mkdir(parents=True, exist_ok=True)