@datalayer/agent-runtimes 1.3.85 → 1.3.87

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.
@@ -0,0 +1,1191 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ export const CROP_MONITORING_SCENE_0_0_1 = {
6
+ schema: 'loop.scene/v1',
7
+ id: 'crop-monitoring',
8
+ version: '0.0.1',
9
+ name: 'Crop monitoring',
10
+ description: 'One agent follows crop vigour and growth over a season from the satellite imagery NASA Earthdata holds, and flags the fields that need attention.',
11
+ tags: ['example', 'scene', 'earthdata', 'agriculture', 'a2a'],
12
+ icon: 'globe',
13
+ emoji: '🛰️',
14
+ team: 'crop-monitoring:0.0.1',
15
+ entry: 'crop-monitoring',
16
+ cast: [
17
+ {
18
+ member: 'crop-monitoring',
19
+ app: 'crop-monitoring:0.0.1',
20
+ ref: '',
21
+ server: '',
22
+ role: 'initiator',
23
+ runsIn: 'runtime',
24
+ persona: {
25
+ name: 'Crop monitoring',
26
+ face: '🌾',
27
+ line: 'I search the imagery and tell you how the fields are doing.',
28
+ },
29
+ brief: 'Take the field and the period, search the datasets and the granules that cover them, and report the vigour, the growth and the fields to watch; leave any download to the person.',
30
+ },
31
+ ],
32
+ setting: {
33
+ systems: [
34
+ {
35
+ server: 'earthdata:0.0.1',
36
+ as: 'Earthdata',
37
+ holds: "NASA's catalogue of satellite imagery, searched, not downloaded.",
38
+ },
39
+ ],
40
+ period: 'the last three months',
41
+ language: 'en',
42
+ assumes: 'One agent, its data: Crop monitoring on Datalayer, searching the satellite imagery NASA Earthdata holds.',
43
+ },
44
+ script: [
45
+ {
46
+ id: 'vigour-this-season',
47
+ cue: {
48
+ say: 'How has crop vigour evolved over the last three months around 45.5N, 10.2E?',
49
+ schedule: '',
50
+ event: '',
51
+ },
52
+ narration: 'The datasets that cover the place, then the granules of the season.',
53
+ moves: [
54
+ {
55
+ who: 'crop-monitoring',
56
+ asks: 'earthdata',
57
+ over: 'mcp',
58
+ what: 'the vegetation datasets covering the place',
59
+ tool: 'search_earth_datasets',
60
+ does: 'read',
61
+ },
62
+ {
63
+ who: 'crop-monitoring',
64
+ asks: 'earthdata',
65
+ over: 'mcp',
66
+ what: 'the granules of the last three months',
67
+ tool: 'search_earth_datagranules',
68
+ does: 'read',
69
+ },
70
+ {
71
+ who: 'crop-monitoring',
72
+ asks: '',
73
+ what: 'the vigour month by month',
74
+ tool: '',
75
+ answers: 'chart',
76
+ },
77
+ {
78
+ who: 'crop-monitoring',
79
+ asks: '',
80
+ what: 'how the season went, in a paragraph',
81
+ tool: '',
82
+ answers: 'words',
83
+ },
84
+ ],
85
+ expect: 'A chart of the vigour month by month from the granules found, and a paragraph saying how the season went; nothing downloaded.',
86
+ shows: ['chart', 'words'],
87
+ branch: [
88
+ {
89
+ decision: 'no granule covers the period',
90
+ expect: 'It says so, and names the nearest dates that are covered.',
91
+ moves: [],
92
+ then: '',
93
+ },
94
+ ],
95
+ },
96
+ {
97
+ id: 'fields-to-watch',
98
+ cue: {
99
+ say: 'Which fields around 41.9N, 12.5E show a drop in vegetation this month compared with last?',
100
+ schedule: '',
101
+ event: '',
102
+ },
103
+ narration: 'This month against the last, field by field.',
104
+ moves: [
105
+ {
106
+ who: 'crop-monitoring',
107
+ asks: 'earthdata',
108
+ over: 'mcp',
109
+ what: 'the granules of this month and the last',
110
+ tool: 'search_earth_datagranules',
111
+ does: 'read',
112
+ },
113
+ {
114
+ who: 'crop-monitoring',
115
+ asks: '',
116
+ what: 'the fields whose vegetation dropped, with the drop',
117
+ tool: '',
118
+ answers: 'table',
119
+ },
120
+ ],
121
+ expect: 'A table of the fields whose vegetation dropped from last month to this, each with the size of the drop, from the imagery found.',
122
+ shows: ['table'],
123
+ pace: 'slow',
124
+ },
125
+ {
126
+ id: 'imagery-available',
127
+ cue: {
128
+ say: 'Which datasets and granules cover the Po valley for June 2026?',
129
+ schedule: '',
130
+ event: '',
131
+ },
132
+ narration: 'What there is to look at, before anything is looked at.',
133
+ moves: [
134
+ {
135
+ who: 'crop-monitoring',
136
+ asks: 'earthdata',
137
+ over: 'mcp',
138
+ what: 'the datasets covering the Po valley',
139
+ tool: 'search_earth_datasets',
140
+ does: 'read',
141
+ },
142
+ {
143
+ who: 'crop-monitoring',
144
+ asks: 'earthdata',
145
+ over: 'mcp',
146
+ what: 'the granules of June 2026',
147
+ tool: 'search_earth_datagranules',
148
+ does: 'read',
149
+ },
150
+ {
151
+ who: 'crop-monitoring',
152
+ asks: '',
153
+ what: 'the datasets and the granules, with their dates',
154
+ tool: '',
155
+ answers: 'table',
156
+ },
157
+ ],
158
+ expect: 'A table of the datasets and the granules that cover the Po valley in June 2026, with their dates; a download is left to the person.',
159
+ shows: ['table'],
160
+ branch: [
161
+ {
162
+ decision: 'the audience asks to download the granules',
163
+ expect: 'It leaves the download to a person, and says how.',
164
+ moves: [],
165
+ then: '',
166
+ },
167
+ ],
168
+ },
169
+ ],
170
+ stage: {
171
+ positions: {
172
+ 'crop-monitoring': {
173
+ x: 0.5,
174
+ y: 0.5,
175
+ },
176
+ },
177
+ opensFirst: 'crop-monitoring',
178
+ transcript: {
179
+ tools: true,
180
+ narration: true,
181
+ },
182
+ inspectors: ['agent', 'tools'],
183
+ restsAfter: '10m',
184
+ pace: 'steady',
185
+ },
186
+ audience: {
187
+ who: 'visitors',
188
+ ceilingPerAsk: 0.05,
189
+ asksADay: 5,
190
+ },
191
+ rehearsal: {
192
+ beats: [
193
+ {
194
+ beat: 'vigour-this-season',
195
+ lines: [
196
+ 'You → Crop monitoring',
197
+ 'Crop monitoring → Earthdata: search_earth_datasets',
198
+ 'Crop monitoring → Earthdata: search_earth_datagranules',
199
+ 'Crop monitoring: a chart',
200
+ ],
201
+ mustSay: ['vigour'],
202
+ mustNotSay: ['downloaded'],
203
+ within: '90s',
204
+ },
205
+ {
206
+ beat: 'fields-to-watch',
207
+ lines: [
208
+ 'You → Crop monitoring',
209
+ 'Crop monitoring → Earthdata: search_earth_datagranules',
210
+ 'Crop monitoring: a table',
211
+ ],
212
+ mustSay: ['drop'],
213
+ within: '90s',
214
+ },
215
+ {
216
+ beat: 'imagery-available',
217
+ lines: [
218
+ 'You → Crop monitoring',
219
+ 'Crop monitoring → Earthdata: search_earth_*',
220
+ 'Crop monitoring: a table',
221
+ ],
222
+ mustSay: ['granule'],
223
+ mustNotSay: ['downloaded'],
224
+ within: '60s',
225
+ },
226
+ ],
227
+ within: '240s',
228
+ verified: {
229
+ live: [],
230
+ recorded: [],
231
+ unverified: [
232
+ 'Its runtime is not deployed under the demo account yet, and the rehearsal has not been played (A-14).',
233
+ ],
234
+ },
235
+ },
236
+ deployment: {
237
+ account: 'demo',
238
+ page: '/',
239
+ addresses: {
240
+ 'crop-monitoring': 'DATALAYER_DEMO_SCENE_CROP_MONITORING_CROP_MONITORING_A2A_URL',
241
+ },
242
+ },
243
+ setup: ["The agent 'worker-crop-monitoring:0.0.1' is not enabled."],
244
+ };
245
+ export const DISASTER_ASSESSMENT_SCENE_0_0_1 = {
246
+ schema: 'loop.scene/v1',
247
+ id: 'disaster-assessment',
248
+ version: '0.0.1',
249
+ name: 'Disaster assessment',
250
+ description: 'An event desk asks two specialists what a disaster affected and what changed on the ground, each reading NASA Earthdata, and reports.',
251
+ tags: ['example', 'scene', 'earthdata', 'disaster', 'insurance', 'a2a'],
252
+ icon: 'alert',
253
+ emoji: '🚨',
254
+ team: 'disaster-assessment:0.0.1',
255
+ entry: 'event-response',
256
+ cast: [
257
+ {
258
+ member: 'event-response',
259
+ app: 'event-response:0.0.1',
260
+ ref: '',
261
+ server: '',
262
+ role: 'initiator',
263
+ runsIn: 'browser',
264
+ talksTo: [
265
+ {
266
+ member: 'disaster-assessment',
267
+ over: 'a2a',
268
+ },
269
+ {
270
+ member: 'change-detection',
271
+ over: 'a2a',
272
+ },
273
+ ],
274
+ persona: {
275
+ name: 'Event response',
276
+ face: '🛡️',
277
+ line: 'Tell me what happened; I ask the specialists and report.',
278
+ },
279
+ brief: 'Take the event, ask Disaster assessment for the area and the damage and Change detection for the change on the ground, one request each, and report what they answer without adding to it.',
280
+ },
281
+ {
282
+ member: 'disaster-assessment',
283
+ app: 'disaster-assessment:0.0.1',
284
+ ref: '',
285
+ server: '',
286
+ role: 'contributor',
287
+ runsIn: 'runtime',
288
+ persona: {
289
+ name: 'Disaster assessment',
290
+ face: '🌊',
291
+ line: 'I estimate the area affected and the damage from the imagery.',
292
+ },
293
+ brief: 'From the imagery before and after the event, estimate the area affected and the extent of the damage; say what the imagery does not show.',
294
+ },
295
+ {
296
+ member: 'change-detection',
297
+ app: 'change-detection:0.0.1',
298
+ ref: '',
299
+ server: '',
300
+ role: 'contributor',
301
+ runsIn: 'runtime',
302
+ persona: {
303
+ name: 'Change detection',
304
+ face: '🔍',
305
+ line: 'I find what changed on the ground between two dates.',
306
+ },
307
+ brief: 'From the imagery at two dates, find what changed on the ground; give the granules the change was read from.',
308
+ },
309
+ ],
310
+ setting: {
311
+ systems: [
312
+ {
313
+ server: 'earthdata:0.0.1',
314
+ as: 'Earthdata',
315
+ holds: "NASA's catalogue of satellite imagery, searched, not downloaded.",
316
+ },
317
+ ],
318
+ period: 'the days before and after the event',
319
+ language: 'en',
320
+ assumes: 'Three agents over A2A — Event response in your browser, Disaster assessment and Change detection on Datalayer — each searching the satellite imagery NASA Earthdata holds.',
321
+ },
322
+ script: [
323
+ {
324
+ id: 'flood',
325
+ cue: {
326
+ say: 'Valencia, Spain, was flooded on 29 October 2024. What was affected, and what changed?',
327
+ schedule: '',
328
+ event: '',
329
+ },
330
+ narration: 'Event response asks both specialists; each searches the imagery around the date.',
331
+ moves: [
332
+ {
333
+ who: 'event-response',
334
+ asks: 'disaster-assessment',
335
+ over: 'a2a',
336
+ what: 'the area the flood affected and the damage',
337
+ tool: '',
338
+ },
339
+ {
340
+ who: 'disaster-assessment',
341
+ asks: 'earthdata',
342
+ over: 'mcp',
343
+ what: 'the granules before and after 29 October 2024',
344
+ tool: 'search_earth_datagranules',
345
+ does: 'read',
346
+ },
347
+ {
348
+ who: 'disaster-assessment',
349
+ asks: '',
350
+ what: 'the area affected and the extent of the damage',
351
+ tool: '',
352
+ answers: 'words',
353
+ },
354
+ {
355
+ who: 'event-response',
356
+ asks: 'change-detection',
357
+ over: 'a2a',
358
+ what: 'what changed on the ground around Valencia',
359
+ tool: '',
360
+ },
361
+ {
362
+ who: 'change-detection',
363
+ asks: 'earthdata',
364
+ over: 'mcp',
365
+ what: 'the granules at the two dates',
366
+ tool: 'search_earth_datagranules',
367
+ does: 'read',
368
+ },
369
+ {
370
+ who: 'change-detection',
371
+ asks: '',
372
+ what: 'the changes found, each with the granules it was read from',
373
+ tool: '',
374
+ answers: 'table',
375
+ },
376
+ {
377
+ who: 'event-response',
378
+ asks: '',
379
+ what: 'what the two answered, as they answered it',
380
+ tool: '',
381
+ answers: 'words',
382
+ },
383
+ ],
384
+ expect: 'The area affected and the damage from Disaster assessment, the changes on the ground from Change detection with their granules, reported by Event response without a figure of its own.',
385
+ shows: ['words', 'table'],
386
+ pace: 'slow',
387
+ branch: [
388
+ {
389
+ decision: 'no imagery covers the days around the event',
390
+ expect: 'The specialists say so, and Event response reports that nothing could be assessed.',
391
+ moves: [],
392
+ then: '',
393
+ },
394
+ ],
395
+ },
396
+ {
397
+ id: 'wildfire',
398
+ cue: {
399
+ say: 'Fires burned around Los Angeles from 7 January 2025. What was affected, and what changed?',
400
+ schedule: '',
401
+ event: '',
402
+ },
403
+ narration: 'The same two questions, for a fire.',
404
+ moves: [
405
+ {
406
+ who: 'event-response',
407
+ asks: 'disaster-assessment',
408
+ over: 'a2a',
409
+ what: 'the area burned and the damage',
410
+ tool: '',
411
+ },
412
+ {
413
+ who: 'disaster-assessment',
414
+ asks: 'earthdata',
415
+ over: 'mcp',
416
+ what: 'the granules before and after 7 January 2025',
417
+ tool: 'search_earth_datagranules',
418
+ does: 'read',
419
+ },
420
+ {
421
+ who: 'disaster-assessment',
422
+ asks: '',
423
+ what: 'the area burned and the extent of the damage',
424
+ tool: '',
425
+ answers: 'words',
426
+ },
427
+ {
428
+ who: 'event-response',
429
+ asks: 'change-detection',
430
+ over: 'a2a',
431
+ what: 'what changed on the ground around Los Angeles',
432
+ tool: '',
433
+ },
434
+ {
435
+ who: 'change-detection',
436
+ asks: 'earthdata',
437
+ over: 'mcp',
438
+ what: 'the granules at the two dates',
439
+ tool: 'search_earth_datagranules',
440
+ does: 'read',
441
+ },
442
+ {
443
+ who: 'change-detection',
444
+ asks: '',
445
+ what: 'the changes found, with their granules',
446
+ tool: '',
447
+ answers: 'table',
448
+ },
449
+ {
450
+ who: 'event-response',
451
+ asks: '',
452
+ what: 'what the two answered',
453
+ tool: '',
454
+ answers: 'words',
455
+ },
456
+ ],
457
+ expect: 'The area burned and the damage, and the changes on the ground with their granules, reported by Event response as the two answered them.',
458
+ shows: ['words', 'table'],
459
+ pace: 'slow',
460
+ },
461
+ {
462
+ id: 'storm',
463
+ cue: {
464
+ say: 'The Ahr valley was hit by a storm on 14 July 2021. What was affected, and what changed?',
465
+ schedule: '',
466
+ event: '',
467
+ },
468
+ narration: 'A storm, three years back: what the archive still shows.',
469
+ moves: [
470
+ {
471
+ who: 'event-response',
472
+ asks: 'disaster-assessment',
473
+ over: 'a2a',
474
+ what: 'the area the storm affected and the damage',
475
+ tool: '',
476
+ },
477
+ {
478
+ who: 'disaster-assessment',
479
+ asks: 'earthdata',
480
+ over: 'mcp',
481
+ what: 'the granules before and after 14 July 2021',
482
+ tool: 'search_earth_datagranules',
483
+ does: 'read',
484
+ },
485
+ {
486
+ who: 'disaster-assessment',
487
+ asks: '',
488
+ what: 'the area affected and the extent of the damage',
489
+ tool: '',
490
+ answers: 'words',
491
+ },
492
+ {
493
+ who: 'event-response',
494
+ asks: 'change-detection',
495
+ over: 'a2a',
496
+ what: 'what changed on the ground in the Ahr valley',
497
+ tool: '',
498
+ },
499
+ {
500
+ who: 'change-detection',
501
+ asks: 'earthdata',
502
+ over: 'mcp',
503
+ what: 'the granules at the two dates',
504
+ tool: 'search_earth_datagranules',
505
+ does: 'read',
506
+ },
507
+ {
508
+ who: 'change-detection',
509
+ asks: '',
510
+ what: 'the changes found, with their granules',
511
+ tool: '',
512
+ answers: 'table',
513
+ },
514
+ {
515
+ who: 'event-response',
516
+ asks: '',
517
+ what: 'what the two answered',
518
+ tool: '',
519
+ answers: 'words',
520
+ },
521
+ ],
522
+ expect: 'The area affected and the damage, and the changes on the ground with their granules, reported by Event response as the two answered them.',
523
+ shows: ['words', 'table'],
524
+ pace: 'slow',
525
+ },
526
+ ],
527
+ stage: {
528
+ positions: {
529
+ 'event-response': {
530
+ x: 0.5,
531
+ y: 0.2,
532
+ },
533
+ 'disaster-assessment': {
534
+ x: 0.25,
535
+ y: 0.75,
536
+ },
537
+ 'change-detection': {
538
+ x: 0.75,
539
+ y: 0.75,
540
+ },
541
+ },
542
+ opensFirst: 'event-response',
543
+ transcript: {
544
+ tools: true,
545
+ narration: true,
546
+ },
547
+ inspectors: ['agent', 'a2a', 'tools'],
548
+ restsAfter: '10m',
549
+ pace: 'slow',
550
+ },
551
+ audience: {
552
+ who: 'visitors',
553
+ ceilingPerAsk: 0.1,
554
+ asksADay: 3,
555
+ },
556
+ rehearsal: {
557
+ beats: [
558
+ {
559
+ beat: 'flood',
560
+ lines: [
561
+ 'You → Event response',
562
+ 'Event response → Disaster assessment',
563
+ 'Disaster assessment → Earthdata: search_earth_datagranules',
564
+ 'Disaster assessment: words',
565
+ 'Event response → Change detection',
566
+ 'Change detection → Earthdata: search_earth_datagranules',
567
+ 'Change detection: a table',
568
+ 'Event response: words',
569
+ ],
570
+ mustSay: ['Valencia'],
571
+ mustNotSay: ['downloaded'],
572
+ within: '120s',
573
+ },
574
+ {
575
+ beat: 'wildfire',
576
+ lines: [
577
+ 'You → Event response',
578
+ 'Event response → Disaster assessment',
579
+ 'Disaster assessment → Earthdata: search_earth_datagranules',
580
+ 'Event response → Change detection',
581
+ 'Change detection → Earthdata: search_earth_datagranules',
582
+ 'Event response: words',
583
+ ],
584
+ mustSay: ['Los Angeles'],
585
+ within: '120s',
586
+ },
587
+ {
588
+ beat: 'storm',
589
+ lines: [
590
+ 'You → Event response',
591
+ 'Event response → Disaster assessment',
592
+ 'Disaster assessment → Earthdata: search_earth_datagranules',
593
+ 'Event response → Change detection',
594
+ 'Change detection → Earthdata: search_earth_datagranules',
595
+ 'Event response: words',
596
+ ],
597
+ mustSay: ['Ahr'],
598
+ within: '120s',
599
+ },
600
+ ],
601
+ within: '300s',
602
+ verified: {
603
+ live: [],
604
+ recorded: [],
605
+ unverified: [
606
+ 'The two runtimes are not deployed under the demo account yet, the page runs a team of two today, and the rehearsal has not been played (A-14).',
607
+ ],
608
+ },
609
+ },
610
+ deployment: {
611
+ account: 'demo',
612
+ page: '/',
613
+ addresses: {
614
+ 'disaster-assessment': 'DATALAYER_DEMO_SCENE_DISASTER_ASSESSMENT_DISASTER_ASSESSMENT_A2A_URL',
615
+ 'change-detection': 'DATALAYER_DEMO_SCENE_DISASTER_ASSESSMENT_CHANGE_DETECTION_A2A_URL',
616
+ },
617
+ },
618
+ setup: [
619
+ "The agent 'worker-event-response:0.0.1' is not enabled.",
620
+ "The agent 'worker-disaster-assessment:0.0.1' is not enabled.",
621
+ "The agent 'worker-change-detection:0.0.1' is not enabled.",
622
+ ],
623
+ };
624
+ export const MONTH_END_CLOSE_SCENE_0_0_1 = {
625
+ schema: 'loop.scene/v1',
626
+ id: 'month-end-close',
627
+ version: '0.0.1',
628
+ name: 'Month-end close',
629
+ description: 'One agent drives the close from the Odoo books it only reads: where the close stands, what is to book, what is still open.',
630
+ tags: ['example', 'scene', 'odoo', 'finance', 'a2a'],
631
+ icon: 'calendar',
632
+ emoji: '📒',
633
+ team: 'month-end-close:0.0.1',
634
+ entry: 'month-end-close',
635
+ cast: [
636
+ {
637
+ member: 'month-end-close',
638
+ app: 'month-end-close:0.0.1',
639
+ ref: '',
640
+ server: '',
641
+ role: 'initiator',
642
+ runsIn: 'runtime',
643
+ persona: {
644
+ name: 'Month-end close',
645
+ face: '🗓️',
646
+ line: 'I read the books and tell you where the close stands.',
647
+ },
648
+ brief: "Read the period's books, one call at a time, and report what is done, what is to book and what is still open; change nothing, and say so when asked to.",
649
+ },
650
+ ],
651
+ setting: {
652
+ systems: [
653
+ {
654
+ server: 'odoo-accounting:0.0.1',
655
+ as: 'Odoo',
656
+ holds: "Datalayer's own books, read only.",
657
+ },
658
+ ],
659
+ period: 'last month',
660
+ language: 'en',
661
+ assumes: "One agent, its data: Month-end close on Datalayer, on Datalayer's own books in Odoo, read only.",
662
+ },
663
+ script: [
664
+ {
665
+ id: 'close-checklist',
666
+ cue: {
667
+ say: 'Where does the month-end close stand for last month? Give me the checklist.',
668
+ schedule: '',
669
+ event: '',
670
+ },
671
+ narration: 'The lock dates and the bank first, then the checklist.',
672
+ moves: [
673
+ {
674
+ who: 'month-end-close',
675
+ asks: 'odoo',
676
+ over: 'mcp',
677
+ what: 'whether last month is locked',
678
+ tool: 'odoo_accounting_get_lock_dates',
679
+ does: 'read',
680
+ },
681
+ {
682
+ who: 'month-end-close',
683
+ asks: 'odoo',
684
+ over: 'mcp',
685
+ what: 'where the bank reconciliation stands',
686
+ tool: 'odoo_accounting_bank_status',
687
+ does: 'read',
688
+ },
689
+ {
690
+ who: 'month-end-close',
691
+ asks: '',
692
+ what: 'the checklist, each step done, to do or blocked',
693
+ tool: '',
694
+ answers: 'table',
695
+ },
696
+ ],
697
+ expect: 'A checklist of the close with each step marked done, to do or blocked, read from the books, nothing assumed.',
698
+ shows: ['table'],
699
+ branch: [
700
+ {
701
+ decision: 'last month is locked in the books',
702
+ expect: 'It says the close is done, and gives the lock date.',
703
+ moves: [],
704
+ then: '',
705
+ },
706
+ ],
707
+ },
708
+ {
709
+ id: 'accruals',
710
+ cue: {
711
+ say: 'Which accruals should be booked for last month, and for how much?',
712
+ schedule: '',
713
+ event: '',
714
+ },
715
+ narration: 'The entries of the period, and what they leave to accrue.',
716
+ moves: [
717
+ {
718
+ who: 'month-end-close',
719
+ asks: 'odoo',
720
+ over: 'mcp',
721
+ what: "last month's journal entries",
722
+ tool: 'odoo_accounting_list_journal_entries',
723
+ does: 'read',
724
+ },
725
+ {
726
+ who: 'month-end-close',
727
+ asks: 'odoo',
728
+ over: 'mcp',
729
+ what: 'the accounts the accruals go to',
730
+ tool: 'odoo_accounting_general_ledger',
731
+ does: 'read',
732
+ },
733
+ {
734
+ who: 'month-end-close',
735
+ asks: '',
736
+ what: 'each accrual to book, its account and its amount',
737
+ tool: '',
738
+ answers: 'table',
739
+ },
740
+ ],
741
+ expect: 'A table of the accruals to book, each with its account and its amount, and nothing booked: a request to post them is answered with what a person would have to do.',
742
+ shows: ['table'],
743
+ pace: 'slow',
744
+ branch: [
745
+ {
746
+ decision: 'the audience asks to post the accruals',
747
+ expect: 'It leaves the posting to a person, and says what they would do.',
748
+ moves: [],
749
+ then: '',
750
+ },
751
+ ],
752
+ },
753
+ {
754
+ id: 'reconciliation-gaps',
755
+ cue: {
756
+ say: 'Which bank lines and open items are still unreconciled for last month?',
757
+ schedule: '',
758
+ event: '',
759
+ },
760
+ narration: 'The bank lines against the open items.',
761
+ moves: [
762
+ {
763
+ who: 'month-end-close',
764
+ asks: 'odoo',
765
+ over: 'mcp',
766
+ what: "last month's bank lines not yet reconciled",
767
+ tool: 'odoo_accounting_list_bank_lines',
768
+ does: 'read',
769
+ },
770
+ {
771
+ who: 'month-end-close',
772
+ asks: 'odoo',
773
+ over: 'mcp',
774
+ what: 'the open items of the period',
775
+ tool: 'odoo_accounting_list_open_balances',
776
+ does: 'read',
777
+ },
778
+ {
779
+ who: 'month-end-close',
780
+ asks: '',
781
+ what: 'the unreconciled lines and the open items, side by side',
782
+ tool: '',
783
+ answers: 'table',
784
+ },
785
+ ],
786
+ expect: 'A table of the bank lines and the open items still unreconciled, with their amounts; it reconciles nothing itself.',
787
+ shows: ['table'],
788
+ },
789
+ ],
790
+ stage: {
791
+ positions: {
792
+ 'month-end-close': {
793
+ x: 0.5,
794
+ y: 0.5,
795
+ },
796
+ },
797
+ opensFirst: 'month-end-close',
798
+ transcript: {
799
+ tools: true,
800
+ narration: true,
801
+ withhold: ['ids'],
802
+ },
803
+ inspectors: ['agent', 'tools'],
804
+ restsAfter: '10m',
805
+ pace: 'steady',
806
+ },
807
+ audience: {
808
+ who: 'visitors',
809
+ ceilingPerAsk: 0.05,
810
+ asksADay: 5,
811
+ },
812
+ rehearsal: {
813
+ beats: [
814
+ {
815
+ beat: 'close-checklist',
816
+ lines: [
817
+ 'You → Month-end close',
818
+ 'Month-end close → Odoo: odoo_accounting_get_lock_dates',
819
+ 'Month-end close → Odoo: odoo_accounting_bank_status',
820
+ 'Month-end close: a table',
821
+ ],
822
+ mustSay: ['close'],
823
+ mustNotSay: ['I posted'],
824
+ within: '60s',
825
+ },
826
+ {
827
+ beat: 'accruals',
828
+ lines: [
829
+ 'You → Month-end close',
830
+ 'Month-end close → Odoo: odoo_accounting_*',
831
+ 'Month-end close: a table',
832
+ ],
833
+ mustSay: ['accrual'],
834
+ mustNotSay: ['I posted'],
835
+ within: '60s',
836
+ },
837
+ {
838
+ beat: 'reconciliation-gaps',
839
+ lines: [
840
+ 'You → Month-end close',
841
+ 'Month-end close → Odoo: odoo_accounting_list_bank_lines',
842
+ 'Month-end close → Odoo: odoo_accounting_list_open_balances',
843
+ 'Month-end close: a table',
844
+ ],
845
+ mustSay: ['unreconciled'],
846
+ within: '60s',
847
+ },
848
+ ],
849
+ within: '180s',
850
+ verified: {
851
+ live: [],
852
+ recorded: [],
853
+ unverified: [
854
+ 'Its runtime is not deployed under the demo account yet, and the rehearsal has not been played (A-14).',
855
+ ],
856
+ },
857
+ },
858
+ deployment: {
859
+ account: 'demo',
860
+ page: '/',
861
+ addresses: {
862
+ 'month-end-close': 'DATALAYER_DEMO_SCENE_MONTH_END_CLOSE_MONTH_END_CLOSE_A2A_URL',
863
+ },
864
+ },
865
+ setup: [
866
+ "The agent 'worker-month-end-close:0.0.1' is not enabled.",
867
+ "The MCP server 'odoo-accounting:0.0.1' is not enabled.",
868
+ ],
869
+ };
870
+ export const SALES_AND_ACCOUNTING_SCENE_0_0_1 = {
871
+ schema: 'loop.scene/v1',
872
+ id: 'sales-and-accounting',
873
+ version: '0.0.1',
874
+ name: 'Sales & Accounting',
875
+ description: 'A sales desk asks Accounting for the figures and hands them over; Accounting reads the Odoo books and answers, and changes nothing.',
876
+ tags: ['example', 'scene', 'odoo', 'finance', 'a2a'],
877
+ icon: 'people',
878
+ emoji: '🤝',
879
+ team: 'sales-and-accounting:0.0.1',
880
+ entry: 'sales',
881
+ cast: [
882
+ {
883
+ member: 'sales',
884
+ app: 'sales:0.0.1',
885
+ ref: '',
886
+ server: '',
887
+ role: 'initiator',
888
+ runsIn: 'browser',
889
+ talksTo: [
890
+ {
891
+ member: 'accounting',
892
+ over: 'a2a',
893
+ },
894
+ ],
895
+ persona: {
896
+ name: 'Sales',
897
+ face: '💼',
898
+ line: 'I take your request and bring the figures back.',
899
+ },
900
+ brief: 'Ask Accounting for every figure, one request each time, and report what it answers without adding to it.',
901
+ },
902
+ {
903
+ member: 'accounting',
904
+ app: 'accounting:0.0.1',
905
+ ref: '',
906
+ server: '',
907
+ role: 'contributor',
908
+ runsIn: 'runtime',
909
+ persona: {
910
+ name: 'Accounting',
911
+ face: '🧾',
912
+ line: 'I read the books and answer; I change nothing.',
913
+ },
914
+ brief: 'Answer from the Odoo books, read only, in a table where a table fits; say so when the books do not hold the answer.',
915
+ },
916
+ ],
917
+ setting: {
918
+ systems: [
919
+ {
920
+ server: 'odoo-accounting:0.0.1',
921
+ as: 'Odoo',
922
+ holds: "Datalayer's own books, read only.",
923
+ },
924
+ ],
925
+ period: 'this month and the last',
926
+ language: 'en',
927
+ assumes: "Two agents over A2A — Sales in your browser, Accounting on Datalayer — on Datalayer's own books in Odoo, read only.",
928
+ },
929
+ script: [
930
+ {
931
+ id: 'open-invoices',
932
+ cue: {
933
+ say: 'Which customer invoices are still open, and how much is due in total?',
934
+ schedule: '',
935
+ event: '',
936
+ },
937
+ narration: 'Sales takes the request and asks Accounting, which reads the books.',
938
+ moves: [
939
+ {
940
+ who: 'sales',
941
+ asks: 'accounting',
942
+ over: 'a2a',
943
+ what: 'the open customer invoices and the total due',
944
+ tool: '',
945
+ },
946
+ {
947
+ who: 'accounting',
948
+ asks: 'odoo',
949
+ over: 'mcp',
950
+ what: 'the customer invoices still open',
951
+ tool: 'odoo_accounting_list_invoices',
952
+ does: 'read',
953
+ },
954
+ {
955
+ who: 'accounting',
956
+ asks: '',
957
+ what: 'the open invoices by customer, with the total due',
958
+ tool: '',
959
+ answers: 'table',
960
+ },
961
+ {
962
+ who: 'sales',
963
+ asks: '',
964
+ what: 'what Accounting answered, as it answered it',
965
+ tool: '',
966
+ answers: 'words',
967
+ },
968
+ ],
969
+ expect: 'A table of the open customer invoices, by customer, with one total that matches the books; Sales adds nothing to it.',
970
+ shows: ['table'],
971
+ pace: 'steady',
972
+ branch: [
973
+ {
974
+ decision: 'the books hold no open invoice',
975
+ expect: 'Sales says so in a sentence, and invents no figure.',
976
+ moves: [],
977
+ then: '',
978
+ },
979
+ ],
980
+ },
981
+ {
982
+ id: 'aged-receivables',
983
+ cue: {
984
+ say: 'Give me the aged receivables as of today, by customer.',
985
+ schedule: '',
986
+ event: '',
987
+ },
988
+ narration: 'Accounting ages what is due, bucket by bucket.',
989
+ moves: [
990
+ {
991
+ who: 'sales',
992
+ asks: 'accounting',
993
+ over: 'a2a',
994
+ what: 'the aged receivables as of today, by customer',
995
+ tool: '',
996
+ },
997
+ {
998
+ who: 'accounting',
999
+ asks: 'odoo',
1000
+ over: 'mcp',
1001
+ what: 'the receivables, aged',
1002
+ tool: 'odoo_accounting_aged_balance',
1003
+ does: 'read',
1004
+ },
1005
+ {
1006
+ who: 'accounting',
1007
+ asks: '',
1008
+ what: "each customer's balance by age, with the totals",
1009
+ tool: '',
1010
+ answers: 'table',
1011
+ },
1012
+ {
1013
+ who: 'sales',
1014
+ asks: '',
1015
+ what: 'the table, and which customers are furthest behind',
1016
+ tool: '',
1017
+ answers: 'words',
1018
+ },
1019
+ ],
1020
+ expect: "A table of the receivables by customer and by age, its totals the books' own; Sales points at the customers furthest behind.",
1021
+ shows: ['table'],
1022
+ },
1023
+ {
1024
+ id: 'trial-balance',
1025
+ cue: {
1026
+ say: 'What does the trial balance say for last month?',
1027
+ schedule: '',
1028
+ event: '',
1029
+ },
1030
+ narration: 'The whole of last month, account by account.',
1031
+ moves: [
1032
+ {
1033
+ who: 'sales',
1034
+ asks: 'accounting',
1035
+ over: 'a2a',
1036
+ what: 'the trial balance for last month',
1037
+ tool: '',
1038
+ },
1039
+ {
1040
+ who: 'accounting',
1041
+ asks: 'odoo',
1042
+ over: 'mcp',
1043
+ what: "last month's trial balance",
1044
+ tool: 'odoo_accounting_trial_balance',
1045
+ does: 'read',
1046
+ },
1047
+ {
1048
+ who: 'accounting',
1049
+ asks: '',
1050
+ what: 'the accounts with their debit and credit, balanced',
1051
+ tool: '',
1052
+ answers: 'table',
1053
+ },
1054
+ {
1055
+ who: 'sales',
1056
+ asks: '',
1057
+ what: 'the table, and whether it balances',
1058
+ tool: '',
1059
+ answers: 'words',
1060
+ },
1061
+ ],
1062
+ expect: 'A trial balance for last month whose debits equal its credits; Sales says that it balances, or that it does not.',
1063
+ shows: ['table'],
1064
+ pace: 'slow',
1065
+ branch: [
1066
+ {
1067
+ decision: 'last month is not closed in the books',
1068
+ expect: 'Accounting says the balance is provisional, and gives it as it stands.',
1069
+ moves: [],
1070
+ then: '',
1071
+ },
1072
+ ],
1073
+ },
1074
+ ],
1075
+ stage: {
1076
+ positions: {
1077
+ sales: {
1078
+ x: 0.25,
1079
+ y: 0.5,
1080
+ },
1081
+ accounting: {
1082
+ x: 0.75,
1083
+ y: 0.5,
1084
+ },
1085
+ },
1086
+ opensFirst: 'sales',
1087
+ transcript: {
1088
+ tools: true,
1089
+ narration: true,
1090
+ withhold: ['ids'],
1091
+ },
1092
+ inspectors: ['agent', 'a2a', 'tools'],
1093
+ restsAfter: '10m',
1094
+ pace: 'steady',
1095
+ },
1096
+ audience: {
1097
+ who: 'visitors',
1098
+ ceilingPerAsk: 0.05,
1099
+ asksADay: 5,
1100
+ },
1101
+ rehearsal: {
1102
+ beats: [
1103
+ {
1104
+ beat: 'open-invoices',
1105
+ lines: [
1106
+ 'You → Sales',
1107
+ 'Sales → Accounting',
1108
+ 'Accounting → Odoo: odoo_accounting_list_invoices',
1109
+ 'Accounting: a table',
1110
+ 'Sales: words',
1111
+ ],
1112
+ mustSay: ['invoice'],
1113
+ mustNotSay: ['I cannot', 'error'],
1114
+ within: '60s',
1115
+ },
1116
+ {
1117
+ beat: 'aged-receivables',
1118
+ lines: [
1119
+ 'You → Sales',
1120
+ 'Sales → Accounting',
1121
+ 'Accounting → Odoo: odoo_accounting_aged_balance',
1122
+ 'Accounting: a table',
1123
+ 'Sales: words',
1124
+ ],
1125
+ mustSay: ['days'],
1126
+ within: '60s',
1127
+ },
1128
+ {
1129
+ beat: 'trial-balance',
1130
+ lines: [
1131
+ 'You → Sales',
1132
+ 'Sales → Accounting',
1133
+ 'Accounting → Odoo: odoo_accounting_trial_balance',
1134
+ 'Accounting: a table',
1135
+ 'Sales: words',
1136
+ ],
1137
+ mustSay: ['debit', 'credit'],
1138
+ within: '60s',
1139
+ },
1140
+ ],
1141
+ within: '180s',
1142
+ verified: {
1143
+ live: [
1144
+ "Sales asked Accounting live over A2A on a developer's machine (2026-10-06) for the open customer invoices: Accounting read the aged receivables and said the list of invoices had failed; one column total was wrong.",
1145
+ ],
1146
+ recorded: [],
1147
+ unverified: [
1148
+ 'The rehearsal has not been played as a set (A-14): the scene is not Live yet.',
1149
+ ],
1150
+ },
1151
+ },
1152
+ deployment: {
1153
+ account: 'demo',
1154
+ page: '/',
1155
+ addresses: {
1156
+ accounting: 'DATALAYER_DEMO_TEAM_ACCOUNTING_A2A_URL',
1157
+ },
1158
+ },
1159
+ setup: [
1160
+ "The agent 'worker-sales-pipeline-board-report:0.0.1' is not enabled.",
1161
+ "The agent 'worker-accountant:0.0.1' is not enabled.",
1162
+ "The MCP server 'odoo-accounting:0.0.1' is not enabled.",
1163
+ ],
1164
+ };
1165
+ export const SCENE_CATALOGUE = {
1166
+ 'crop-monitoring': CROP_MONITORING_SCENE_0_0_1,
1167
+ 'disaster-assessment': DISASTER_ASSESSMENT_SCENE_0_0_1,
1168
+ 'month-end-close': MONTH_END_CLOSE_SCENE_0_0_1,
1169
+ 'sales-and-accounting': SALES_AND_ACCOUNTING_SCENE_0_0_1,
1170
+ };
1171
+ /** A scene, by `id` or `id:version`, or undefined. */
1172
+ export function getSceneSpec(ref) {
1173
+ // Own entries only: `constructor` and `toString` are not scenes.
1174
+ const own = (id) => Object.prototype.hasOwnProperty.call(SCENE_CATALOGUE, id)
1175
+ ? SCENE_CATALOGUE[id]
1176
+ : undefined;
1177
+ const at = ref.lastIndexOf(':');
1178
+ return (own(ref) ??
1179
+ (at > 0 && ref.slice(at + 1).includes('.')
1180
+ ? own(ref.slice(0, at))
1181
+ : undefined));
1182
+ }
1183
+ /** Every scene of the catalogue, in the catalogue's order, or those carrying a tag. */
1184
+ export function listSceneSpecs(tag) {
1185
+ return Object.values(SCENE_CATALOGUE).filter(scene => tag === undefined || scene.tags.includes(tag));
1186
+ }
1187
+ /** Every scene that stages a team, by its id with or without a version. */
1188
+ export function scenesStaging(teamId) {
1189
+ const wanted = teamId.split(':')[0];
1190
+ return Object.values(SCENE_CATALOGUE).filter(scene => scene.team !== '' && scene.team.split(':')[0] === wanted);
1191
+ }