@datalayer/agent-runtimes 1.3.81 → 1.3.82

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 (66) hide show
  1. package/lib/chat/assistant/SpeechBalloon.js +16 -1
  2. package/lib/chat/base/ChatBase.js +40 -4
  3. package/lib/chat/indicators/McpStatusIndicator.d.ts +1 -1
  4. package/lib/chat/indicators/McpStatusIndicator.js +13 -6
  5. package/lib/chat/indicators/SkillsStatusIndicator.d.ts +1 -1
  6. package/lib/chat/indicators/SkillsStatusIndicator.js +7 -3
  7. package/lib/chat/prompt/InputPrompt.d.ts +4 -1
  8. package/lib/chat/prompt/InputPrompt.js +2 -2
  9. package/lib/chat/prompt/InputPromptBase.d.ts +4 -1
  10. package/lib/chat/prompt/InputPromptBase.js +2 -2
  11. package/lib/chat/prompt/InputPromptLexical.d.ts +7 -1
  12. package/lib/chat/prompt/InputPromptLexical.js +5 -3
  13. package/lib/components/teams/index.d.ts +1 -0
  14. package/lib/components/teams/index.js +1 -0
  15. package/lib/components/teams/usePeerSandbox.d.ts +13 -0
  16. package/lib/components/teams/usePeerSandbox.js +59 -0
  17. package/lib/examples/AgentA2ATeamExample.js +7 -2
  18. package/lib/loop/apps/AppComposer.d.ts +31 -0
  19. package/lib/loop/apps/AppComposer.js +67 -0
  20. package/lib/loop/apps/AppRenderer.d.ts +16 -5
  21. package/lib/loop/apps/AppRenderer.js +49 -16
  22. package/lib/loop/apps/appspec.d.ts +8 -1
  23. package/lib/loop/apps/appspec.js +87 -0
  24. package/lib/loop/apps/checks.js +108 -1
  25. package/lib/loop/apps/composer.d.ts +37 -0
  26. package/lib/loop/apps/composer.js +61 -0
  27. package/lib/loop/apps/index.d.ts +2 -0
  28. package/lib/loop/apps/index.js +2 -0
  29. package/lib/loop/core/index.d.ts +31 -0
  30. package/lib/loop/core/index.js +22 -0
  31. package/lib/loop/embed/AppEmbed.d.ts +46 -7
  32. package/lib/loop/embed/AppEmbed.js +108 -26
  33. package/lib/loop/embed/LoopEmbed.js +2 -0
  34. package/lib/loop/embed/element.js +19 -5
  35. package/lib/loop/embed/embedConfig.d.ts +10 -1
  36. package/lib/loop/embed/embedConfig.js +12 -1
  37. package/lib/loop/embed/embedSession.d.ts +79 -0
  38. package/lib/loop/embed/embedSession.js +136 -0
  39. package/lib/loop/embed/embedTheme.d.ts +12 -9
  40. package/lib/loop/embed/embedTheme.js +32 -22
  41. package/lib/loop/index.d.ts +4 -3
  42. package/lib/loop/index.js +4 -3
  43. package/lib/loop/plugins/agent-capacity/index.d.ts +3 -1
  44. package/lib/loop/plugins/agent-capacity/index.js +2 -1
  45. package/lib/loop/plugins/chat/ChatView.js +31 -4
  46. package/lib/loop/plugins/chat/index.d.ts +6 -1
  47. package/lib/loop/presets.d.ts +3 -1
  48. package/lib/loop/presets.js +2 -1
  49. package/lib/loop/shell/LoopWorkspace.js +3 -0
  50. package/lib/protocols/BrowserAgentAdapter.d.ts +18 -0
  51. package/lib/protocols/BrowserAgentAdapter.js +35 -2
  52. package/lib/runtimes/__tests__/fixtures/accounting-agent-card.json +8 -0
  53. package/lib/runtimes/browser/a2aPeer.d.ts +11 -0
  54. package/lib/runtimes/browser/a2aPeer.js +15 -1
  55. package/lib/runtimes/browser/protocol.d.ts +3 -0
  56. package/lib/runtimes/browser/protocol.js +2 -1
  57. package/lib/specs/apps.js +28 -0
  58. package/lib/specs/appspecSchema.js +191 -0
  59. package/lib/types/agentspecs.d.ts +58 -0
  60. package/lib/types/chat.d.ts +29 -1
  61. package/package.json +1 -1
  62. package/scripts/codegen/generate_agents.py +4 -2
  63. package/scripts/codegen/generate_backend_tools.py +4 -12
  64. package/scripts/codegen/generate_cogs.py +3 -1
  65. package/scripts/codegen/generate_ui_plugins.py +22 -9
  66. package/scripts/voice/transcribe.mjs +29 -21
@@ -36,6 +36,19 @@ import { ClientFactory, ClientFactoryOptions, DefaultAgentCardResolver, JsonRpcT
36
36
  import { jsonSchema, tool } from 'ai';
37
37
  /** Where an A2A agent's card is, under the address it is served at. */
38
38
  export const AGENT_CARD_PATH = '.well-known/agent-card.json';
39
+ /** The extension an agent card carries its face in: its name stays plain. */
40
+ export const A2A_FACE_EXTENSION_URI = 'https://datalayer.ai/extensions/face/v1';
41
+ /** The face an agent card carries, if it carries one. */
42
+ export function faceOfCard(card) {
43
+ const extension = card.capabilities?.extensions?.find(candidate => candidate.uri === A2A_FACE_EXTENSION_URI);
44
+ const params = (extension?.params ?? {});
45
+ if (typeof params.emoji !== 'string' || !params.emoji) {
46
+ return undefined;
47
+ }
48
+ return typeof params.avatar === 'string' && params.avatar
49
+ ? { emoji: params.emoji, avatar: params.avatar }
50
+ : { emoji: params.emoji };
51
+ }
39
52
  /** A fetch that sends the key as a bearer token, beside the caller's headers. */
40
53
  function withKey(base, key) {
41
54
  if (!key) {
@@ -69,7 +82,8 @@ export async function connectA2APeer(options) {
69
82
  if (!skill) {
70
83
  throw new Error(`${card.name || root} offers no skill on its agent card: there is nothing to ask it.`);
71
84
  }
72
- return { client, card, skill };
85
+ const face = faceOfCard(card);
86
+ return face ? { client, card, skill, face } : { client, card, skill };
73
87
  }
74
88
  /** The media types an answer in words comes in: the answer itself, never an artifact of its own. */
75
89
  export const TEXT_MEDIA_TYPES = [
@@ -12,6 +12,7 @@ import type { ProtocolConfig } from '../../types/protocol';
12
12
  import type { AgentStreamSubagentPayload } from '../../types/stream';
13
13
  import type { FrontendToolDefinition } from '../../types/tools';
14
14
  import type { TeamContextSharing } from '../../types/teams';
15
+ import type { AppModeEffect } from '../../types/agentspecs';
15
16
  import type { BrowserModelOptions } from './model';
16
17
  import type { BrowserSubagent } from './subagents';
17
18
  export type BrowserProtocolOptions = {
@@ -37,6 +38,8 @@ export type BrowserProtocolOptions = {
37
38
  sharing?: TeamContextSharing;
38
39
  /** Told what a delegated run does, as it does it — see `subagentTools`. */
39
40
  onSubagentEvent?: (event: AgentStreamSubagentPayload) => void;
41
+ /** What the modes a run is sent in tell it (LOOP P-19); none when unsaid. */
42
+ modeEffect?: (chosen: Record<string, string>) => AppModeEffect;
40
43
  };
41
44
  /**
42
45
  * The protocol config for an agent whose loop runs in this page.
@@ -16,7 +16,7 @@
16
16
  * keystroke, so an omitted flag reads as a chat that silently ignores typing.
17
17
  */
18
18
  export function browserProtocolConfig(options) {
19
- const { agentId, instructions, model, frontendTools, inference, subagents, sharing, onSubagentEvent, } = options;
19
+ const { agentId, instructions, model, frontendTools, inference, subagents, sharing, onSubagentEvent, modeEffect, } = options;
20
20
  return {
21
21
  type: 'browser-vercel-ai',
22
22
  endpoint: '',
@@ -30,6 +30,7 @@ export function browserProtocolConfig(options) {
30
30
  subagents,
31
31
  sharing,
32
32
  onSubagentEvent,
33
+ modeEffect,
33
34
  },
34
35
  };
35
36
  }
package/lib/specs/apps.js CHANGED
@@ -64,6 +64,8 @@ export const ACCOUNTING_APP_0_0_1 = {
64
64
  message: 'Give the trial balance for last month.',
65
65
  },
66
66
  ],
67
+ commands: [],
68
+ modes: [],
67
69
  settings: [],
68
70
  components: [],
69
71
  assistant: 'wizard',
@@ -186,6 +188,8 @@ export const CUSTOMER_INTERVIEW_APP_0_0_1 = {
186
188
  message: 'Interview me about my first week with the product.',
187
189
  },
188
190
  ],
191
+ commands: [],
192
+ modes: [],
189
193
  settings: [
190
194
  {
191
195
  id: 'language',
@@ -309,6 +313,8 @@ export const DATA_QUALITY_APP_0_0_1 = {
309
313
  accent: 'green',
310
314
  welcome: '',
311
315
  starters: [],
316
+ commands: [],
317
+ modes: [],
312
318
  settings: [],
313
319
  components: [
314
320
  'Card',
@@ -475,6 +481,8 @@ export const DECIDE_APP_0_0_1 = {
475
481
  message: "Score how positive this review is from 1 to 5: 'Setup took an hour, but support answered fast and it works.'",
476
482
  },
477
483
  ],
484
+ commands: [],
485
+ modes: [],
478
486
  settings: [],
479
487
  components: [],
480
488
  assistant: 'wizard',
@@ -628,6 +636,8 @@ export const INBOX_TRIAGE_APP_0_0_1 = {
628
636
  message: 'What arrived since yesterday, and what did you do with it?',
629
637
  },
630
638
  ],
639
+ commands: [],
640
+ modes: [],
631
641
  settings: [],
632
642
  components: [],
633
643
  voice: {
@@ -761,6 +771,8 @@ export const MODEL_CHOICE_APP_0_0_1 = {
761
771
  accent: 'green',
762
772
  welcome: '',
763
773
  starters: [],
774
+ commands: [],
775
+ modes: [],
764
776
  settings: [],
765
777
  components: [
766
778
  'Card',
@@ -989,6 +1001,8 @@ export const PIPELINE_REPORT_APP_0_0_1 = {
989
1001
  message: "What changed in the pipeline since last week's report?",
990
1002
  },
991
1003
  ],
1004
+ commands: [],
1005
+ modes: [],
992
1006
  settings: [],
993
1007
  components: [
994
1008
  'Card',
@@ -1247,6 +1261,8 @@ export const QUOTE_CALCULATOR_APP_0_0_1 = {
1247
1261
  accent: 'sun',
1248
1262
  welcome: '',
1249
1263
  starters: [],
1264
+ commands: [],
1265
+ modes: [],
1250
1266
  settings: [
1251
1267
  {
1252
1268
  id: 'seats',
@@ -1506,6 +1522,8 @@ export const REPORT_FROM_A_FILE_APP_0_0_1 = {
1506
1522
  accent: 'sky',
1507
1523
  welcome: '',
1508
1524
  starters: [],
1525
+ commands: [],
1526
+ modes: [],
1509
1527
  settings: [
1510
1528
  {
1511
1529
  id: 'report',
@@ -1757,6 +1775,8 @@ export const SALES_APP_0_0_1 = {
1757
1775
  message: 'What does the trial balance say for last month?',
1758
1776
  },
1759
1777
  ],
1778
+ commands: [],
1779
+ modes: [],
1760
1780
  settings: [],
1761
1781
  components: [],
1762
1782
  assistant: 'paperclip',
@@ -1862,6 +1882,8 @@ export const SHIP_OR_FIX_APP_0_0_1 = {
1862
1882
  accent: 'green',
1863
1883
  welcome: '',
1864
1884
  starters: [],
1885
+ commands: [],
1886
+ modes: [],
1865
1887
  settings: [],
1866
1888
  components: [
1867
1889
  'Card',
@@ -2038,6 +2060,8 @@ export const SUPPLIER_COMPARISON_APP_0_0_1 = {
2038
2060
  accent: 'green',
2039
2061
  welcome: '',
2040
2062
  starters: [],
2063
+ commands: [],
2064
+ modes: [],
2041
2065
  settings: [],
2042
2066
  components: [
2043
2067
  'Card',
@@ -2214,6 +2238,8 @@ export const SUPPORT_DESK_APP_0_0_1 = {
2214
2238
  message: 'What is the difference between the Team and the Business plan?',
2215
2239
  },
2216
2240
  ],
2241
+ commands: [],
2242
+ modes: [],
2217
2243
  settings: [
2218
2244
  {
2219
2245
  id: 'product',
@@ -2470,6 +2496,8 @@ export const WEB_RESEARCH_APP_0_0_1 = {
2470
2496
  message: 'Is it true that most data science projects never reach production? Find the primary source.',
2471
2497
  },
2472
2498
  ],
2499
+ commands: [],
2500
+ modes: [],
2473
2501
  settings: [
2474
2502
  {
2475
2503
  id: 'depth',
@@ -52,6 +52,33 @@ export const APPSPEC_SCHEMA = {
52
52
  title: 'AppChecks',
53
53
  type: 'object',
54
54
  },
55
+ AppCommand: {
56
+ additionalProperties: false,
57
+ description: "A slash command the user picks in the composer (LOOP P-19).\n\nTyping `/` lists the application's commands; picking one sends its\n`prompt`, `{input}` replaced by the words typed after the command.",
58
+ properties: {
59
+ name: {
60
+ description: 'What follows the slash: lower-case letters, digits and hyphens, a letter first (`summarise`)',
61
+ pattern: '^[a-z](?:[a-z0-9-]{0,30}[a-z0-9])?$',
62
+ title: 'Name',
63
+ type: 'string',
64
+ },
65
+ description: {
66
+ description: "What the composer's menu says it does",
67
+ minLength: 1,
68
+ title: 'Description',
69
+ type: 'string',
70
+ },
71
+ prompt: {
72
+ description: "What is sent when it is picked: `{input}` stands for the words typed after it; without `{input}`, those words follow the prompt. An application's code answers `/<name> {input}` itself (`@app.command`)",
73
+ minLength: 1,
74
+ title: 'Prompt',
75
+ type: 'string',
76
+ },
77
+ },
78
+ required: ['name', 'description', 'prompt'],
79
+ title: 'AppCommand',
80
+ type: 'object',
81
+ },
55
82
  AppComputer: {
56
83
  additionalProperties: false,
57
84
  description: 'What the application may do on its own computer. Each is off until it is turned on.',
@@ -269,6 +296,18 @@ export const APPSPEC_SCHEMA = {
269
296
  default: 'green',
270
297
  description: "The application's one colour",
271
298
  },
299
+ theme: {
300
+ anyOf: [
301
+ {
302
+ $ref: '#/$defs/AppTheme',
303
+ },
304
+ {
305
+ type: 'null',
306
+ },
307
+ ],
308
+ default: null,
309
+ description: "The theme it runs in by default, at its address, embedded, in the Studio's Preview and as an example: a `variant` and, optionally, a colour `mode`. The person's own when unsaid. Its `accent` colours the `loop` theme only",
310
+ },
272
311
  welcome: {
273
312
  default: '',
274
313
  description: 'What the application says first',
@@ -283,6 +322,22 @@ export const APPSPEC_SCHEMA = {
283
322
  title: 'Starters',
284
323
  type: 'array',
285
324
  },
325
+ commands: {
326
+ description: 'Slash commands the user picks in the composer: typing `/` lists them (LOOP P-19)',
327
+ items: {
328
+ $ref: '#/$defs/AppCommand',
329
+ },
330
+ title: 'Commands',
331
+ type: 'array',
332
+ },
333
+ modes: {
334
+ description: 'Mode switches in the composer: the option picked goes with every run, its instructions told to the agent and its model run on (LOOP P-19)',
335
+ items: {
336
+ $ref: '#/$defs/AppMode',
337
+ },
338
+ title: 'Modes',
339
+ type: 'array',
340
+ },
286
341
  settings: {
287
342
  description: 'What the user may set',
288
343
  items: {
@@ -362,6 +417,95 @@ export const APPSPEC_SCHEMA = {
362
417
  title: 'AppKind',
363
418
  type: 'string',
364
419
  },
420
+ AppMode: {
421
+ additionalProperties: false,
422
+ description: 'A mode switch in the composer (LOOP P-19): the person picks one of its options.',
423
+ properties: {
424
+ id: {
425
+ description: 'Its id, the key a run says its option under',
426
+ pattern: '^[a-z][a-z0-9_-]{0,39}$',
427
+ title: 'Id',
428
+ type: 'string',
429
+ },
430
+ label: {
431
+ description: 'What the switch is called',
432
+ minLength: 1,
433
+ title: 'Label',
434
+ type: 'string',
435
+ },
436
+ options: {
437
+ description: 'Its positions, two at least',
438
+ items: {
439
+ $ref: '#/$defs/AppModeOption',
440
+ },
441
+ minItems: 2,
442
+ title: 'Options',
443
+ type: 'array',
444
+ },
445
+ default: {
446
+ anyOf: [
447
+ {
448
+ type: 'string',
449
+ },
450
+ {
451
+ type: 'null',
452
+ },
453
+ ],
454
+ default: null,
455
+ description: 'The option it starts on; the first when unsaid',
456
+ title: 'Default',
457
+ },
458
+ },
459
+ required: ['id', 'label', 'options'],
460
+ title: 'AppMode',
461
+ type: 'object',
462
+ },
463
+ AppModeOption: {
464
+ additionalProperties: false,
465
+ description: 'One position of a mode switch (LOOP P-19): what the agent is told, the model it runs on.',
466
+ properties: {
467
+ id: {
468
+ description: 'Its id, what a run says it is in',
469
+ pattern: '^[a-z][a-z0-9_-]{0,39}$',
470
+ title: 'Id',
471
+ type: 'string',
472
+ },
473
+ label: {
474
+ description: 'What the switch says',
475
+ minLength: 1,
476
+ title: 'Label',
477
+ type: 'string',
478
+ },
479
+ description: {
480
+ default: '',
481
+ description: 'What it changes, in a sentence',
482
+ title: 'Description',
483
+ type: 'string',
484
+ },
485
+ instructions: {
486
+ default: '',
487
+ description: 'What the agent is told besides its instructions, in every run in this mode',
488
+ title: 'Instructions',
489
+ type: 'string',
490
+ },
491
+ model: {
492
+ anyOf: [
493
+ {
494
+ type: 'string',
495
+ },
496
+ {
497
+ type: 'null',
498
+ },
499
+ ],
500
+ default: null,
501
+ description: "The model a run in this mode runs on, in place of the application's",
502
+ title: 'Model',
503
+ },
504
+ },
505
+ required: ['id', 'label'],
506
+ title: 'AppModeOption',
507
+ type: 'object',
508
+ },
365
509
  AppPermissions: {
366
510
  additionalProperties: false,
367
511
  description: 'What the application may reach beside its connections. Nothing, unless said.',
@@ -667,6 +811,31 @@ export const APPSPEC_SCHEMA = {
667
811
  title: 'AppTests',
668
812
  type: 'object',
669
813
  },
814
+ AppTheme: {
815
+ additionalProperties: false,
816
+ description: 'The theme an application runs in by default (LOOP T-30).',
817
+ properties: {
818
+ variant: {
819
+ $ref: '#/$defs/ThemeVariant',
820
+ description: 'The theme: `datalayer`, `spatial`, `lovely`, `matrix`, `earth`, `sand`, `ivory`, `sun` or `loop`',
821
+ },
822
+ mode: {
823
+ anyOf: [
824
+ {
825
+ $ref: '#/$defs/ThemeMode',
826
+ },
827
+ {
828
+ type: 'null',
829
+ },
830
+ ],
831
+ default: null,
832
+ description: "`light`, `dark` or `auto` (the device's); the person's own when unsaid",
833
+ },
834
+ },
835
+ required: ['variant'],
836
+ title: 'AppTheme',
837
+ type: 'object',
838
+ },
670
839
  AppTrigger: {
671
840
  additionalProperties: false,
672
841
  description: "What starts a worker's work.",
@@ -943,6 +1112,28 @@ export const APPSPEC_SCHEMA = {
943
1112
  title: 'SettingType',
944
1113
  type: 'string',
945
1114
  },
1115
+ ThemeMode: {
1116
+ description: 'The colour mode a theme is worn in: `auto` follows the device.',
1117
+ enum: ['light', 'dark', 'auto'],
1118
+ title: 'ThemeMode',
1119
+ type: 'string',
1120
+ },
1121
+ ThemeVariant: {
1122
+ description: "A theme of Datalayer's, as Appearance names it (LOOP T-30).",
1123
+ enum: [
1124
+ 'datalayer',
1125
+ 'spatial',
1126
+ 'lovely',
1127
+ 'matrix',
1128
+ 'earth',
1129
+ 'sand',
1130
+ 'ivory',
1131
+ 'sun',
1132
+ 'loop',
1133
+ ],
1134
+ title: 'ThemeVariant',
1135
+ type: 'string',
1136
+ },
946
1137
  TriggerType: {
947
1138
  description: 'What starts a worker: the trigger kinds of the catalogue.',
948
1139
  enum: ['schedule', 'event', 'once'],
@@ -386,6 +386,19 @@ export type AppBehaviour = 'do_it' | 'if_asked' | 'ask_first' | 'leave_to_me';
386
386
  export type AppLayout = 'chat' | 'page' | 'split';
387
387
  /** The one colour of an application; everything else is neutral. */
388
388
  export type AppAccent = 'green' | 'rose' | 'sky' | 'lime' | 'sun' | 'violet';
389
+ /** A theme of Appearance's, which an application may run in (LOOP T-30). */
390
+ export type AppThemeVariant = 'datalayer' | 'spatial' | 'lovely' | 'matrix' | 'earth' | 'sand' | 'ivory' | 'sun' | 'loop';
391
+ /** The colour mode it is worn in: `auto` follows the device. */
392
+ export type AppThemeMode = 'light' | 'dark' | 'auto';
393
+ /**
394
+ * The theme an application runs in by default (LOOP T-30): at its address,
395
+ * embedded, in the Studio's Preview and as an example. The person's own
396
+ * when unsaid; the mode, when unsaid, is the person's.
397
+ */
398
+ export interface AppThemeSpec {
399
+ variant: AppThemeVariant;
400
+ mode?: AppThemeMode;
401
+ }
389
402
  /**
390
403
  * The character an application's floating assistant shows (LOOP T-24): the id
391
404
  * an enabled plugin contributes it under to `loop.assistant.character` —
@@ -434,6 +447,44 @@ export interface AppStarterSpec {
434
447
  label: string;
435
448
  message: string;
436
449
  }
450
+ /** A slash command the user picks in the composer (LOOP P-19). */
451
+ export interface AppCommandSpec {
452
+ /** What follows the slash: lower-case letters, digits and hyphens. */
453
+ name: string;
454
+ /** What the composer's menu says it does. */
455
+ description: string;
456
+ /** What picking it sends: `{input}` the words typed after it. */
457
+ prompt: string;
458
+ }
459
+ /** One position of a mode switch (LOOP P-19). */
460
+ export interface AppModeOptionSpec {
461
+ id: string;
462
+ label: string;
463
+ description?: string;
464
+ /** What the agent is told in every run in this mode. */
465
+ instructions?: string;
466
+ /** The model a run in this mode runs on, in place of the application's. */
467
+ model?: string | null;
468
+ }
469
+ /** A mode switch in the composer (LOOP P-19). */
470
+ export interface AppModeSpec {
471
+ id: string;
472
+ label: string;
473
+ /** Its positions, two at least. */
474
+ options: AppModeOptionSpec[];
475
+ /** The option it starts on; the first when unsaid. */
476
+ default?: string | null;
477
+ }
478
+ /**
479
+ * What a run in the modes chosen is told, and the model it runs on (LOOP
480
+ * P-19): agentspecs' `ModeEffect`.
481
+ */
482
+ export interface AppModeEffect {
483
+ /** The instructions of the options chosen, in the order of the modes. */
484
+ instructions: string;
485
+ /** The model an option chosen names; the application's when unsaid. */
486
+ model?: string;
487
+ }
437
488
  export interface AppSettingSpec {
438
489
  id: string;
439
490
  type: 'select' | 'text' | 'toggle' | 'slider' | 'number';
@@ -471,9 +522,16 @@ export interface AppVoiceSpec {
471
522
  /** What the user of an application sees. */
472
523
  export interface AppInterfaceSpec {
473
524
  layout: AppLayout;
525
+ /** Its one colour, over the `loop` theme only. */
474
526
  accent: AppAccent;
527
+ /** The theme it runs in by default; the person's own when unsaid (T-30). */
528
+ theme?: AppThemeSpec;
475
529
  welcome: string;
476
530
  starters: AppStarterSpec[];
531
+ /** Slash commands picked in the composer: typing `/` lists them (P-19). */
532
+ commands: AppCommandSpec[];
533
+ /** Mode switches in the composer, the option picked going with every run (P-19). */
534
+ modes: AppModeSpec[];
477
535
  settings: AppSettingSpec[];
478
536
  /** The components of the catalog the surface may use. */
479
537
  components: string[];
@@ -15,6 +15,7 @@ import type { AgentRuntimeConfig } from './config';
15
15
  import type { ContextSnapshotData } from './context';
16
16
  import type { InputPromptVariant } from '../chat/prompt/InputPromptBase';
17
17
  import type { MentionableAgent } from '../chat/prompt/plugins/AgentMentionPlugin';
18
+ import type { PromptCommand } from '../chat/prompt/plugins/CommandPlugin';
18
19
  import type { Icon } from '@primer/octicons-react';
19
20
  /** One agent a chat may address, as its controls need it. */
20
21
  export interface FooterAgent {
@@ -652,6 +653,21 @@ export type ThemeOverrides = {
652
653
  light?: Record<string, string>;
653
654
  dark?: Record<string, string>;
654
655
  };
656
+ /**
657
+ * A conversation the chat goes on with (LOOP D-13): an embedded
658
+ * application's session, reattached after its page reloaded.
659
+ */
660
+ export type ResumedThread = {
661
+ /** Its id, AG-UI's `threadId`: the session's uid. */
662
+ id: string;
663
+ /** What it holds already; none for a new one. */
664
+ messages: ChatMessage[];
665
+ /**
666
+ * Told the thread a message is first sent on — this one, or a new one
667
+ * after the header's + — so that its host keeps it. A stable function.
668
+ */
669
+ onStarted?: (threadId: string) => void;
670
+ };
655
671
  export interface ChatBaseProps {
656
672
  /**
657
673
  * Hands an imperative send function to the host, once the chat is able to
@@ -722,7 +738,7 @@ export interface ChatBaseProps {
722
738
  /**
723
739
  * External MCP toolsets status data for the MCP indicator.
724
740
  * When provided, the data is forwarded to the McpStatusIndicator
725
- * so it shows live status instead of "No MCP Server defined".
741
+ * so it shows their live status; with none, it is not drawn.
726
742
  */
727
743
  mcpStatusData?: import('./mcp').McpToolsetsStatusResponse | null;
728
744
  /**
@@ -755,6 +771,11 @@ export interface ChatBaseProps {
755
771
  promptVariant?: InputPromptVariant;
756
772
  /** Agents the prompt may address by typing `@`. Lexical only. */
757
773
  mentionableAgents?: MentionableAgent[];
774
+ /**
775
+ * Commands the prompt lists while `/` is typed, before its own: an
776
+ * application's (LOOP P-19). Picking one writes `/<name> `. Lexical only.
777
+ */
778
+ promptCommands?: PromptCommand[];
758
779
  /**
759
780
  * Whether the prompt offers a chooser for who answers.
760
781
  *
@@ -1190,6 +1211,13 @@ export interface ChatBaseProps {
1190
1211
  userId?: string;
1191
1212
  accessToken?: string;
1192
1213
  }>;
1214
+ /**
1215
+ * The conversation to go on with (LOOP D-13): its AG-UI thread — an
1216
+ * application's session — and what it holds, drawn in place of the
1217
+ * runtime's history, which is its agent's and not one session's. Read
1218
+ * when the chat mounts; a new thread (the header's +) is the chat's own.
1219
+ */
1220
+ thread?: ResumedThread;
1193
1221
  /**
1194
1222
  * Runtime ID for conversation persistence.
1195
1223
  * When provided, messages are restored from websocket snapshot data on
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalayer/agent-runtimes",
3
- "version": "1.3.81",
3
+ "version": "1.3.82",
4
4
  "type": "module",
5
5
  "workspaces": [
6
6
  ".",
@@ -410,7 +410,8 @@ from agent_runtimes.types import (
410
410
  for skill in spec.get("skills", [])
411
411
  ]
412
412
  tool_refs = [
413
- versioned_ref(*split_spec_ref(tool)) for tool in spec.get("backend_tools", [])
413
+ versioned_ref(*split_spec_ref(tool))
414
+ for tool in spec.get("backend_tools", [])
414
415
  ]
415
416
  frontend_tool_refs = [
416
417
  versioned_ref(*split_spec_ref(ft))
@@ -1032,7 +1033,8 @@ const FRONTEND_TOOL_MAP: Record<string, any> = {
1032
1033
 
1033
1034
  # Get backend tools - resolve to BackendToolSpec via TOOL_MAP
1034
1035
  tool_ids_list = [
1035
- versioned_ref(*split_spec_ref(sid)) for sid in spec.get("backend_tools", [])
1036
+ versioned_ref(*split_spec_ref(sid))
1037
+ for sid in spec.get("backend_tools", [])
1036
1038
  ]
1037
1039
  if has_tools and tool_ids_list:
1038
1040
  tools_str = ", ".join(f"TOOL_MAP['{tid}']" for tid in tool_ids_list)
@@ -110,9 +110,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
110
110
  for spec in specs:
111
111
  tool_id = spec["id"]
112
112
  version = spec["version"]
113
- const_name = (
114
- f"{tool_id.upper().replace('-', '_')}_BACKEND_TOOL_SPEC{version_suffix(version)}"
115
- )
113
+ const_name = f"{tool_id.upper().replace('-', '_')}_BACKEND_TOOL_SPEC{version_suffix(version)}"
116
114
  runtime = _require_runtime(spec)
117
115
  requires_approval = _requires_approval(spec)
118
116
  timeout = _timeout_hms(spec)
@@ -157,9 +155,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
157
155
  for spec in specs:
158
156
  tool_id = spec["id"]
159
157
  version = spec["version"]
160
- const_name = (
161
- f"{tool_id.upper().replace('-', '_')}_BACKEND_TOOL_SPEC{version_suffix(version)}"
162
- )
158
+ const_name = f"{tool_id.upper().replace('-', '_')}_BACKEND_TOOL_SPEC{version_suffix(version)}"
163
159
  lines.append(f' "{tool_id}": {const_name},')
164
160
 
165
161
  lines.extend(
@@ -215,9 +211,7 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
215
211
  for spec in specs:
216
212
  tool_id = spec["id"]
217
213
  version = spec["version"]
218
- const_name = (
219
- f"{tool_id.upper().replace('-', '_')}_BACKEND_TOOL_SPEC{version_suffix(version)}"
220
- )
214
+ const_name = f"{tool_id.upper().replace('-', '_')}_BACKEND_TOOL_SPEC{version_suffix(version)}"
221
215
  runtime = _require_runtime(spec)
222
216
  requires_approval = _requires_approval(spec)
223
217
  timeout = _timeout_hms(spec)
@@ -264,9 +258,7 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
264
258
  for spec in specs:
265
259
  tool_id = spec["id"]
266
260
  version = spec["version"]
267
- const_name = (
268
- f"{tool_id.upper().replace('-', '_')}_BACKEND_TOOL_SPEC{version_suffix(version)}"
269
- )
261
+ const_name = f"{tool_id.upper().replace('-', '_')}_BACKEND_TOOL_SPEC{version_suffix(version)}"
270
262
  lines.append(f" '{tool_id}': {const_name},")
271
263
 
272
264
  lines.extend(
@@ -208,7 +208,9 @@ def generate_typescript_code(specs: list[dict[str, Any]], specs_dir: Path) -> st
208
208
  # is one directory up, beside the catalogues it imports.
209
209
  head = head.replace("from '../../types'", "from '../types'")
210
210
  head = re.sub(
211
- r"from '\.\./(mcpServers|skills|backendTools|frontendTools)'", r"from './\1'", head
211
+ r"from '\.\./(mcpServers|skills|backendTools|frontendTools)'",
212
+ r"from './\1'",
213
+ head,
212
214
  )
213
215
 
214
216
  lines = [