@datalayer/agent-runtimes 1.3.84 → 1.3.85

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 (141) hide show
  1. package/lib/chat/ChatFloating.d.ts +5 -4
  2. package/lib/chat/ChatFloating.js +8 -6
  3. package/lib/chat/ChatLanguage.d.ts +17 -0
  4. package/lib/chat/ChatLanguage.js +29 -0
  5. package/lib/chat/assistant/AssistantStage.d.ts +4 -4
  6. package/lib/chat/assistant/AssistantStage.js +4 -2
  7. package/lib/chat/assistant/BalloonParts.d.ts +2 -1
  8. package/lib/chat/assistant/BalloonParts.js +12 -5
  9. package/lib/chat/assistant/BalloonVisual.js +3 -1
  10. package/lib/chat/assistant/ConversationBalloon.d.ts +1 -1
  11. package/lib/chat/assistant/ConversationBalloon.js +3 -2
  12. package/lib/chat/assistant/SpeechBalloon.d.ts +1 -1
  13. package/lib/chat/assistant/SpeechBalloon.js +10 -7
  14. package/lib/chat/assistant/toolLine.d.ts +8 -4
  15. package/lib/chat/assistant/toolLine.js +13 -9
  16. package/lib/chat/base/ChatBase.js +14 -11
  17. package/lib/chat/display/EmptyState.js +3 -1
  18. package/lib/chat/index.d.ts +2 -0
  19. package/lib/chat/index.js +3 -0
  20. package/lib/chat/messages/ChatMessages.js +3 -1
  21. package/lib/chat/messages/TurnFooter.js +3 -1
  22. package/lib/chat/prompt/InputPrompt.js +3 -1
  23. package/lib/chat/prompt/InputPromptLexical.js +3 -1
  24. package/lib/chat/prompt/footer/InPromptFooter.js +3 -1
  25. package/lib/chat/tools/ToolApprovalBanner.js +5 -3
  26. package/lib/chat/tools/ToolApprovalDialog.js +5 -3
  27. package/lib/chat/tools/ToolCallDisplay.js +29 -25
  28. package/lib/chat/words.d.ts +102 -0
  29. package/lib/chat/words.js +460 -0
  30. package/lib/components/a2ui/custom/index.d.ts +39 -0
  31. package/lib/components/a2ui/custom/index.js +95 -0
  32. package/lib/components/a2ui/custom/sandbox.d.ts +73 -0
  33. package/lib/components/a2ui/custom/sandbox.js +188 -0
  34. package/lib/components/a2ui/datalayer/index.d.ts +1 -1
  35. package/lib/components/a2ui/datalayer/index.js +1 -1
  36. package/lib/components/a2ui/datalayer/schema.d.ts +10 -0
  37. package/lib/components/a2ui/datalayer/schema.js +12 -1
  38. package/lib/components/a2ui/index.d.ts +1 -0
  39. package/lib/components/a2ui/index.js +1 -0
  40. package/lib/components/a2ui/visibleWhen.d.ts +12 -1
  41. package/lib/components/a2ui/visibleWhen.js +24 -3
  42. package/lib/components/inspector/AgentInspector.d.ts +25 -44
  43. package/lib/components/inspector/AgentInspector.js +27 -215
  44. package/lib/components/inspector/AgentInspectorDialog.d.ts +6 -6
  45. package/lib/components/inspector/AgentInspectorDialog.js +2 -2
  46. package/lib/components/inspector/a2aSpans.d.ts +51 -0
  47. package/lib/components/inspector/a2aSpans.js +373 -0
  48. package/lib/components/inspector/agentSpans.d.ts +120 -0
  49. package/lib/components/inspector/agentSpans.js +253 -0
  50. package/lib/components/inspector/chatSpans.d.ts +36 -0
  51. package/lib/components/inspector/chatSpans.js +50 -0
  52. package/lib/components/inspector/classify.d.ts +11 -8
  53. package/lib/components/inspector/classify.js +23 -24
  54. package/lib/components/inspector/index.d.ts +7 -7
  55. package/lib/components/inspector/index.js +7 -7
  56. package/lib/components/teams/A2ATeamGraph.d.ts +4 -4
  57. package/lib/components/teams/SceneTranscript.d.ts +29 -0
  58. package/lib/components/teams/SceneTranscript.js +54 -0
  59. package/lib/components/teams/SceneView.d.ts +41 -0
  60. package/lib/components/teams/SceneView.js +28 -0
  61. package/lib/components/teams/index.d.ts +3 -0
  62. package/lib/components/teams/index.js +3 -0
  63. package/lib/components/teams/sceneTranscript.d.ts +74 -0
  64. package/lib/components/teams/sceneTranscript.js +294 -0
  65. package/lib/components/teams/teamConnections.d.ts +2 -2
  66. package/lib/components/teams/teamConnections.js +3 -3
  67. package/lib/components/teams/useA2ATeam.d.ts +4 -4
  68. package/lib/components/teams/useA2ATeam.js +50 -46
  69. package/lib/examples/AgentA2ATeamExample.d.ts +10 -9
  70. package/lib/examples/AgentA2ATeamExample.js +70 -60
  71. package/lib/examples/AssistantExample.js +2 -2
  72. package/lib/loop/apps/AppComposer.d.ts +2 -1
  73. package/lib/loop/apps/AppComposer.js +6 -3
  74. package/lib/loop/apps/AppRenderer.d.ts +2 -1
  75. package/lib/loop/apps/AppRenderer.js +34 -25
  76. package/lib/loop/apps/appspec.d.ts +11 -1
  77. package/lib/loop/apps/appspec.js +115 -1
  78. package/lib/loop/apps/checks.d.ts +8 -0
  79. package/lib/loop/apps/checks.js +106 -4
  80. package/lib/loop/apps/customComponents.d.ts +41 -0
  81. package/lib/loop/apps/customComponents.js +260 -0
  82. package/lib/loop/apps/hostTools.d.ts +8 -1
  83. package/lib/loop/apps/hostTools.js +9 -0
  84. package/lib/loop/apps/index.d.ts +1 -0
  85. package/lib/loop/apps/index.js +1 -0
  86. package/lib/loop/core/a2uiComponents.d.ts +10 -0
  87. package/lib/loop/core/a2uiComponents.js +15 -0
  88. package/lib/loop/core/index.d.ts +1 -0
  89. package/lib/loop/core/index.js +3 -0
  90. package/lib/loop/embed/AppEmbed.d.ts +11 -3
  91. package/lib/loop/embed/AppEmbed.js +42 -16
  92. package/lib/loop/embed/element.d.ts +5 -0
  93. package/lib/loop/embed/element.js +61 -5
  94. package/lib/loop/embed/embedConfig.d.ts +8 -0
  95. package/lib/loop/embed/embedConfig.js +18 -0
  96. package/lib/loop/embed/hostBridge.d.ts +28 -2
  97. package/lib/loop/embed/hostBridge.js +22 -2
  98. package/lib/loop/index.d.ts +4 -3
  99. package/lib/loop/index.js +4 -3
  100. package/lib/loop/plugins/a2ui-surface/InlineSurface.d.ts +7 -1
  101. package/lib/loop/plugins/a2ui-surface/InlineSurface.js +7 -2
  102. package/lib/loop/plugins/a2ui-surface/SurfaceToolResult.js +4 -1
  103. package/lib/loop/plugins/a2ui-surface/ownComponents.d.ts +6 -0
  104. package/lib/loop/plugins/a2ui-surface/ownComponents.js +30 -0
  105. package/lib/loop/plugins/app-components/index.d.ts +26 -0
  106. package/lib/loop/plugins/app-components/index.js +56 -0
  107. package/lib/loop/plugins/app-elements/AppElements.js +4 -1
  108. package/lib/loop/plugins/app-page/AppPage.d.ts +9 -0
  109. package/lib/loop/plugins/app-page/AppPage.js +106 -8
  110. package/lib/loop/plugins/app-page/appPageModel.d.ts +17 -1
  111. package/lib/loop/plugins/app-page/appPageModel.js +120 -4
  112. package/lib/loop/plugins/app-page/index.d.ts +1 -0
  113. package/lib/loop/plugins/app-page/index.js +1 -0
  114. package/lib/loop/plugins/app-page/pageRun.d.ts +46 -0
  115. package/lib/loop/plugins/app-page/pageRun.js +111 -0
  116. package/lib/loop/plugins/assistant/index.js +8 -6
  117. package/lib/loop/plugins/chat/ChatView.js +6 -4
  118. package/lib/loop/plugins/chat/index.d.ts +1 -1
  119. package/lib/loop/plugins/chat/index.js +2 -1
  120. package/lib/models/PageTag.d.ts +1 -1
  121. package/lib/runtimes/browser/a2aPeer.d.ts +2 -1
  122. package/lib/specs/actions.js +78 -1
  123. package/lib/specs/apps.d.ts +5 -0
  124. package/lib/specs/apps.js +1384 -231
  125. package/lib/specs/appspecSchema.js +191 -1
  126. package/lib/specs/teams/teams.d.ts +3 -0
  127. package/lib/specs/teams/teams.js +249 -0
  128. package/lib/types/agentspecs.d.ts +70 -0
  129. package/lib/types/chat.d.ts +6 -5
  130. package/lib/types/teams.d.ts +13 -2
  131. package/package.json +2 -2
  132. package/scripts/codegen/generate_apps.py +4 -2
  133. package/scripts/codegen/generate_teams.py +15 -2
  134. package/lib/components/inspector/JsonTree.d.ts +0 -24
  135. package/lib/components/inspector/JsonTree.js +0 -89
  136. package/lib/components/inspector/a2aInspect.d.ts +0 -56
  137. package/lib/components/inspector/a2aInspect.js +0 -478
  138. package/lib/components/inspector/agentInspector.d.ts +0 -179
  139. package/lib/components/inspector/agentInspector.js +0 -267
  140. package/lib/components/inspector/chatInspect.d.ts +0 -38
  141. package/lib/components/inspector/chatInspect.js +0 -65
package/lib/specs/apps.js CHANGED
@@ -147,6 +147,302 @@ export const ACCOUNTING_APP_0_0_1 = {
147
147
  "The MCP server 'odoo-accounting:0.0.1' is not enabled.",
148
148
  ],
149
149
  };
150
+ export const CHANGE_DETECTION_APP_0_0_1 = {
151
+ schema: 'loop.app/v1',
152
+ id: 'change-detection',
153
+ version: '0.0.1',
154
+ name: 'Change detection',
155
+ kind: 'chat',
156
+ description: 'Finds what changed on the ground between two dates — land use, vegetation, water, built-up area — from the satellite imagery NASA Earthdata holds, which it searches and reads, with the granules behind each change it reports.',
157
+ owner: 'Datalayer <info@datalayer.io>',
158
+ agent: 'worker-change-detection:0.0.1',
159
+ team: '',
160
+ instructions: 'You detect change from satellite imagery. Requests usually come from the Event response application over A2A, and you answer them from NASA Earthdata, which you reach through the earthdata tools: search the datasets that observe the surface at the place asked, then the granules at the first date and at the second, and compare what they show. You download nothing: when data must be fetched, describe it and write the script a person would run. Answer with the change itself: the place and the two dates, each change you read — where, what kind, how large — and how confident you are, the dataset and granule behind each one. When a request does not say the place or the dates, say what you need. When Earthdata holds nothing for it, say so plainly and do not fill the gap.',
161
+ model: '',
162
+ skills: [],
163
+ backendTools: [],
164
+ tools: [],
165
+ context: [],
166
+ contents: [],
167
+ connections: [
168
+ {
169
+ server: 'earthdata:0.0.1',
170
+ access: 'read',
171
+ as: 'owner',
172
+ only: [],
173
+ },
174
+ ],
175
+ rules: [
176
+ {
177
+ action: 'Search and read the imagery',
178
+ appliesTo: ['read'],
179
+ behaviour: 'do_it',
180
+ },
181
+ {
182
+ action: 'Download files to the runtime',
183
+ appliesTo: ['write'],
184
+ behaviour: 'ask_first',
185
+ },
186
+ ],
187
+ permissions: {
188
+ spaces: [],
189
+ computer: {
190
+ browse: false,
191
+ files: false,
192
+ shell: false,
193
+ },
194
+ },
195
+ interface: {
196
+ layout: 'chat',
197
+ accent: 'violet',
198
+ welcome: "Give me a place and two dates, and I'll tell you what changed on the ground between them from the satellite imagery NASA Earthdata holds. I search and read; I download nothing.",
199
+ starters: [
200
+ {
201
+ label: 'Change between two dates',
202
+ message: 'What changed around 39.5N, 0.4W between 1 October and 15 November 2024?',
203
+ },
204
+ {
205
+ label: 'Water extent',
206
+ message: 'How did the water extent change around the Ahr valley between 10 and 20 July 2021?',
207
+ },
208
+ {
209
+ label: 'Imagery at two dates',
210
+ message: 'Which granules cover Los Angeles on 1 January and 15 January 2025?',
211
+ },
212
+ ],
213
+ commands: [],
214
+ modes: [],
215
+ profiles: [],
216
+ language: 'en',
217
+ translations: {},
218
+ components: [],
219
+ assistant: 'cat',
220
+ voice: {
221
+ enabled: false,
222
+ input: 'push_to_talk',
223
+ output: 'on_request',
224
+ voice: '',
225
+ language: '',
226
+ where: 'auto',
227
+ },
228
+ outputs: ['text/markdown', 'application/x-ipynb+json'],
229
+ },
230
+ tests: {
231
+ readyAt: 0.8,
232
+ evalset: '',
233
+ cases: [
234
+ {
235
+ ask: 'What changed around 39.5N, 0.4W between 1 October and 15 November 2024?',
236
+ expect: 'It searches the datasets and the granules at both dates with the earthdata tools and answers with each change it read, its kind and its size, and the granules behind it.',
237
+ },
238
+ {
239
+ ask: 'Which granules cover Los Angeles on 1 January and 15 January 2025?',
240
+ expect: 'It lists the granules at both dates, with their datasets, and downloads nothing.',
241
+ },
242
+ {
243
+ ask: 'Download the granules at both dates.',
244
+ expect: 'It does not download them. It writes the script a person would run, and says what it would fetch.',
245
+ },
246
+ {
247
+ ask: 'What changed?',
248
+ expect: 'It asks where and between which dates, before it searches.',
249
+ },
250
+ ],
251
+ verified: {
252
+ live: [],
253
+ recorded: [],
254
+ unverified: [
255
+ 'It has not been asked live over A2A yet: its agent is set up, not enabled, on this machine; the earthdata server is enabled.',
256
+ 'Its tests have not been run as a set: no validation run is attached to it.',
257
+ ],
258
+ },
259
+ },
260
+ record: {
261
+ keepFor: '30_days',
262
+ include: ['conversations'],
263
+ suggestTests: false,
264
+ retentionDays: 30,
265
+ },
266
+ checks: {
267
+ guards: [],
268
+ gates: [],
269
+ track: '',
270
+ code: [],
271
+ },
272
+ deployment: {
273
+ hosted: {
274
+ visibility: 'private',
275
+ slug: '',
276
+ },
277
+ },
278
+ goal: '',
279
+ triggers: [],
280
+ memory: '',
281
+ notifications: [],
282
+ enabled: true,
283
+ unavailable_because: '',
284
+ tags: [
285
+ 'example',
286
+ 'earth-observation',
287
+ 'change-detection',
288
+ 'earthdata',
289
+ 'a2a',
290
+ 'scene',
291
+ ],
292
+ icon: 'telescope',
293
+ emoji: '🔍',
294
+ avatar: '',
295
+ banner: '',
296
+ setup: ["The agent 'worker-change-detection:0.0.1' is not enabled."],
297
+ };
298
+ export const CROP_MONITORING_APP_0_0_1 = {
299
+ schema: 'loop.app/v1',
300
+ id: 'crop-monitoring',
301
+ version: '0.0.1',
302
+ name: 'Crop monitoring',
303
+ kind: 'chat',
304
+ description: 'Tracks crop vigour and growth over time from the satellite imagery NASA Earthdata holds, which it searches and reads, and flags the fields that need attention, with the datasets and granules behind each finding.',
305
+ owner: 'Datalayer <info@datalayer.io>',
306
+ agent: 'worker-crop-monitoring:0.0.1',
307
+ team: '',
308
+ instructions: 'You monitor crops from satellite imagery. You work from NASA Earthdata, which you reach through the earthdata tools: search the datasets that observe vegetation, land surface and moisture, then the granules that cover the field and the period asked, and say what each one shows — its dataset, its dates, its resolution, its cloud cover when it is given. You download nothing: when a person wants the data, describe what to fetch and write the script that fetches it for them to run. Answer with the monitoring itself: the field and the period, the vigour and growth you read across the dates, the fields or the parcels that need attention and why, and the dataset and granule behind each finding. When a request does not say the field or the period, ask before you search. When Earthdata holds nothing for it, say so plainly and do not fill the gap.',
309
+ model: '',
310
+ skills: [],
311
+ backendTools: [],
312
+ tools: [],
313
+ context: [],
314
+ contents: [],
315
+ connections: [
316
+ {
317
+ server: 'earthdata:0.0.1',
318
+ access: 'read',
319
+ as: 'owner',
320
+ only: [],
321
+ },
322
+ ],
323
+ rules: [
324
+ {
325
+ action: 'Search and read the imagery',
326
+ appliesTo: ['read'],
327
+ behaviour: 'do_it',
328
+ },
329
+ {
330
+ action: 'Download files to the runtime',
331
+ appliesTo: ['write'],
332
+ behaviour: 'ask_first',
333
+ },
334
+ ],
335
+ permissions: {
336
+ spaces: [],
337
+ computer: {
338
+ browse: false,
339
+ files: false,
340
+ shell: false,
341
+ },
342
+ },
343
+ interface: {
344
+ layout: 'chat',
345
+ accent: 'lime',
346
+ welcome: "Give me a field and a period, and I'll follow its crops across the satellite imagery NASA Earthdata holds: vigour, growth and what needs attention. I search and read; I download nothing.",
347
+ starters: [
348
+ {
349
+ label: 'Vigour this season',
350
+ message: 'How has crop vigour evolved over the last three months around 45.5N, 10.2E?',
351
+ },
352
+ {
353
+ label: 'Fields to watch',
354
+ message: 'Which fields around 41.9N, 12.5E show a drop in vegetation this month compared with last?',
355
+ },
356
+ {
357
+ label: 'Imagery available',
358
+ message: 'Which datasets and granules cover the Po valley for June 2026?',
359
+ },
360
+ ],
361
+ commands: [],
362
+ modes: [],
363
+ profiles: [],
364
+ language: 'en',
365
+ translations: {},
366
+ components: [],
367
+ assistant: 'eyes',
368
+ voice: {
369
+ enabled: false,
370
+ input: 'push_to_talk',
371
+ output: 'on_request',
372
+ voice: '',
373
+ language: '',
374
+ where: 'auto',
375
+ },
376
+ outputs: ['text/markdown', 'application/x-ipynb+json'],
377
+ },
378
+ tests: {
379
+ readyAt: 0.8,
380
+ evalset: '',
381
+ cases: [
382
+ {
383
+ ask: 'How has crop vigour evolved over the last three months around 45.5N, 10.2E?',
384
+ expect: 'It searches the vegetation datasets and their granules over the period with the earthdata tools and answers with what the imagery shows across the dates, naming each dataset and granule.',
385
+ },
386
+ {
387
+ ask: 'Which datasets and granules cover the Po valley for June 2026?',
388
+ expect: 'It lists the datasets and granules it found, with their dates, and downloads nothing.',
389
+ },
390
+ {
391
+ ask: 'Download the granules for me.',
392
+ expect: 'It does not download them. It writes the script a person would run, and says what it would fetch.',
393
+ },
394
+ {
395
+ ask: 'Which fields are at risk on Mars?',
396
+ expect: 'It says Earthdata holds no such imagery, and invents nothing.',
397
+ },
398
+ ],
399
+ verified: {
400
+ live: [],
401
+ recorded: [],
402
+ unverified: [
403
+ 'It has not run live yet: its agent is set up, not enabled, on this machine; the earthdata server is enabled.',
404
+ 'Its tests have not been run as a set: no validation run is attached to it.',
405
+ ],
406
+ },
407
+ },
408
+ record: {
409
+ keepFor: '30_days',
410
+ include: ['conversations'],
411
+ suggestTests: false,
412
+ retentionDays: 30,
413
+ },
414
+ checks: {
415
+ guards: [],
416
+ gates: [],
417
+ track: '',
418
+ code: [],
419
+ },
420
+ deployment: {
421
+ hosted: {
422
+ visibility: 'private',
423
+ slug: '',
424
+ },
425
+ },
426
+ goal: '',
427
+ triggers: [],
428
+ memory: '',
429
+ notifications: [],
430
+ enabled: true,
431
+ unavailable_because: '',
432
+ tags: [
433
+ 'example',
434
+ 'earth-observation',
435
+ 'agriculture',
436
+ 'earthdata',
437
+ 'a2a',
438
+ 'scene',
439
+ ],
440
+ icon: 'telescope',
441
+ emoji: '🌾',
442
+ avatar: '',
443
+ banner: '',
444
+ setup: ["The agent 'worker-crop-monitoring:0.0.1' is not enabled."],
445
+ };
150
446
  export const CUSTOMER_INTERVIEW_APP_0_0_1 = {
151
447
  schema: 'loop.app/v1',
152
448
  id: 'customer-interview',
@@ -330,18 +626,317 @@ export const DATA_QUALITY_APP_0_0_1 = {
330
626
  profiles: [],
331
627
  language: 'en',
332
628
  translations: {},
333
- components: [
334
- 'Card',
335
- 'Column',
336
- 'Row',
337
- 'List',
338
- 'Tabs',
339
- 'Text',
340
- 'Slider',
341
- 'ChoicePicker',
342
- 'TextField',
343
- 'Button',
344
- ],
629
+ components: [
630
+ 'Card',
631
+ 'Column',
632
+ 'Row',
633
+ 'List',
634
+ 'Tabs',
635
+ 'Text',
636
+ 'Slider',
637
+ 'ChoicePicker',
638
+ 'TextField',
639
+ 'Button',
640
+ ],
641
+ voice: {
642
+ enabled: false,
643
+ input: 'push_to_talk',
644
+ output: 'on_request',
645
+ voice: '',
646
+ language: '',
647
+ where: 'auto',
648
+ },
649
+ outputs: [],
650
+ },
651
+ tests: {
652
+ readyAt: 0.8,
653
+ evalset: '',
654
+ cases: [],
655
+ verified: {
656
+ live: [],
657
+ recorded: [],
658
+ unverified: [
659
+ 'It has not decided live: no dataset has been measured for it.',
660
+ 'It has no test yet: what a good decision looks like has not been written down.',
661
+ ],
662
+ },
663
+ },
664
+ record: {
665
+ keepFor: '1_years',
666
+ include: ['decisions', 'sources', 'checks'],
667
+ suggestTests: false,
668
+ retentionDays: 365,
669
+ },
670
+ checks: {
671
+ guards: [],
672
+ gates: [],
673
+ track: '',
674
+ code: [],
675
+ },
676
+ deployment: {
677
+ hosted: {
678
+ visibility: 'private',
679
+ slug: '',
680
+ },
681
+ },
682
+ goal: '',
683
+ triggers: [],
684
+ memory: '',
685
+ notifications: [],
686
+ decision: {
687
+ question: 'Which anomalies in this dataset should we fix first?',
688
+ alternatives: [],
689
+ criteria: [
690
+ {
691
+ name: 'Rows affected',
692
+ kind: 'metric',
693
+ weight: 2.0,
694
+ instructions: 'How many rows the anomaly touches, from a validation run in the sandbox.',
695
+ options: [],
696
+ direction: 'higher',
697
+ measure: '',
698
+ },
699
+ {
700
+ name: 'Effect on the result',
701
+ kind: 'metric',
702
+ weight: 3.0,
703
+ instructions: 'How far the headline figures move when the anomaly is corrected.',
704
+ options: [],
705
+ direction: 'higher',
706
+ measure: '',
707
+ },
708
+ {
709
+ name: 'Kind of anomaly',
710
+ kind: 'choice',
711
+ weight: 0.0,
712
+ instructions: 'What is this anomaly?',
713
+ options: [
714
+ 'Genuine: a real extreme, to keep',
715
+ 'Outlier: a value far from the rest, to check',
716
+ 'Unit: a unit mismatch',
717
+ 'Missing: a missing value',
718
+ 'Duplicate: the same row twice',
719
+ ],
720
+ direction: 'higher',
721
+ measure: '',
722
+ },
723
+ {
724
+ name: 'Safe to correct automatically',
725
+ kind: 'noul',
726
+ weight: 1.0,
727
+ instructions: 'Can the proposed correction be applied without a person checking each row?',
728
+ options: [],
729
+ direction: 'higher',
730
+ measure: '',
731
+ },
732
+ ],
733
+ minConfidence: 0.0,
734
+ scenarios: [],
735
+ decisionModel: 'cloudflare:gtw/typesafe/jev',
736
+ },
737
+ enabled: true,
738
+ unavailable_because: '',
739
+ tags: ['example', 'decision', 'data-quality'],
740
+ icon: 'filter',
741
+ emoji: '🧹',
742
+ avatar: '',
743
+ banner: '',
744
+ setup: [],
745
+ };
746
+ export const DECIDE_APP_0_0_1 = {
747
+ schema: 'loop.app/v1',
748
+ id: 'decide',
749
+ version: '0.0.1',
750
+ name: 'Decide',
751
+ kind: 'chat',
752
+ description: 'Answers a question about a text — a ticket, a message, a review — by asking Jev a typed decision: yes or no, one of named options, or a score, each with its confidence.',
753
+ owner: 'Datalayer <info@datalayer.io>',
754
+ agent: 'example-simple:0.0.1',
755
+ team: '',
756
+ instructions: 'Answer every question about a text by asking a typed decision with the decide tool: the text as it was given is the state, and the question is one of noul (does a statement hold: yes or no), choice (which of the options named) or score (which step of a scale, lowest first). Then say the answer in plain words with its probability or its confidence, for example "Urgent: yes (0.87)". When the question names no options for a choice or no scale for a score, ask for them rather than inventing them. When nothing was decided, say why in a sentence.',
757
+ model: '',
758
+ skills: [],
759
+ backendTools: ['decide:0.0.1'],
760
+ tools: [],
761
+ context: [],
762
+ contents: [],
763
+ connections: [],
764
+ rules: [
765
+ {
766
+ action: 'Ask a decision',
767
+ appliesTo: ['decide'],
768
+ behaviour: 'do_it',
769
+ },
770
+ ],
771
+ permissions: {
772
+ spaces: [],
773
+ computer: {
774
+ browse: false,
775
+ files: false,
776
+ shell: false,
777
+ },
778
+ },
779
+ interface: {
780
+ layout: 'chat',
781
+ accent: 'sun',
782
+ welcome: 'Give me a text and a question about it. I ask Jev a typed decision — yes or no, a choice, or a score — and tell you the answer with its confidence.',
783
+ starters: [
784
+ {
785
+ label: 'Is it urgent?',
786
+ message: "Is this ticket urgent? 'Help! My payouts have been failing for 3 days.'",
787
+ },
788
+ {
789
+ label: 'Which team?',
790
+ message: "Which team should handle this: 'I was charged twice this month'? Billing, Tech or Sales.",
791
+ },
792
+ {
793
+ label: 'Score a review',
794
+ message: "Score how positive this review is from 1 to 5: 'Setup took an hour, but support answered fast and it works.'",
795
+ },
796
+ ],
797
+ commands: [],
798
+ modes: [],
799
+ profiles: [],
800
+ language: 'en',
801
+ translations: {},
802
+ components: [],
803
+ assistant: 'wizard',
804
+ voice: {
805
+ enabled: false,
806
+ input: 'push_to_talk',
807
+ output: 'on_request',
808
+ voice: '',
809
+ language: '',
810
+ where: 'auto',
811
+ },
812
+ outputs: [],
813
+ },
814
+ tests: {
815
+ readyAt: 0.8,
816
+ evalset: '',
817
+ cases: [
818
+ {
819
+ ask: "Is this ticket urgent? 'Help! My payouts have been failing for 3 days.'",
820
+ expect: 'It calls decide with a noul question and answers yes, with its probability.',
821
+ },
822
+ {
823
+ ask: "Which team should handle this: 'I was charged twice this month'? Billing, Tech or Sales.",
824
+ expect: 'It calls decide with a choice among the three and answers Billing, with its confidence.',
825
+ },
826
+ {
827
+ ask: 'Score this review.',
828
+ expect: 'It asks for the review and the scale rather than inventing them.',
829
+ },
830
+ ],
831
+ verified: {
832
+ live: [],
833
+ recorded: [],
834
+ unverified: [
835
+ 'Its tests have not been run as a set: no validation run is attached to it.',
836
+ ],
837
+ },
838
+ },
839
+ record: {
840
+ keepFor: '30_days',
841
+ include: ['conversations', 'decisions'],
842
+ suggestTests: false,
843
+ retentionDays: 30,
844
+ },
845
+ checks: {
846
+ guards: [],
847
+ gates: [],
848
+ track: '',
849
+ code: [],
850
+ },
851
+ deployment: {
852
+ hosted: {
853
+ visibility: 'private',
854
+ slug: '',
855
+ },
856
+ },
857
+ goal: '',
858
+ triggers: [],
859
+ memory: '',
860
+ notifications: [],
861
+ enabled: true,
862
+ unavailable_because: '',
863
+ tags: ['example', 'decisions', 'jev'],
864
+ icon: 'law',
865
+ emoji: '⚖️',
866
+ avatar: '',
867
+ banner: '',
868
+ setup: [],
869
+ };
870
+ export const DISASTER_ASSESSMENT_APP_0_0_1 = {
871
+ schema: 'loop.app/v1',
872
+ id: 'disaster-assessment',
873
+ version: '0.0.1',
874
+ name: 'Disaster assessment',
875
+ kind: 'chat',
876
+ description: 'Estimates the area a natural disaster affected and the extent of the damage from the satellite imagery NASA Earthdata holds before and after the event, which it searches and reads, with the granules behind each figure.',
877
+ owner: 'Datalayer <info@datalayer.io>',
878
+ agent: 'worker-disaster-assessment:0.0.1',
879
+ team: '',
880
+ instructions: 'You assess disasters from satellite imagery. Requests usually come from the Event response application over A2A, and you answer them from NASA Earthdata, which you reach through the earthdata tools: search the datasets that observe the surface at the place asked, then the granules before the event and after it, and compare what they show. You download nothing: when data must be fetched, describe it and write the script a person would run. Answer with the assessment itself: the event, the place and the dates, the area affected and how you bounded it, the extent of the damage you read and how confident you are, the dataset and granule behind each figure. When a request does not say the event, the place or its date, say what you need. When Earthdata holds nothing for it, say so plainly and do not fill the gap.',
881
+ model: '',
882
+ skills: [],
883
+ backendTools: [],
884
+ tools: [],
885
+ context: [],
886
+ contents: [],
887
+ connections: [
888
+ {
889
+ server: 'earthdata:0.0.1',
890
+ access: 'read',
891
+ as: 'owner',
892
+ only: [],
893
+ },
894
+ ],
895
+ rules: [
896
+ {
897
+ action: 'Search and read the imagery',
898
+ appliesTo: ['read'],
899
+ behaviour: 'do_it',
900
+ },
901
+ {
902
+ action: 'Download files to the runtime',
903
+ appliesTo: ['write'],
904
+ behaviour: 'ask_first',
905
+ },
906
+ ],
907
+ permissions: {
908
+ spaces: [],
909
+ computer: {
910
+ browse: false,
911
+ files: false,
912
+ shell: false,
913
+ },
914
+ },
915
+ interface: {
916
+ layout: 'chat',
917
+ accent: 'rose',
918
+ welcome: "Name an event, a place and a date, and I'll compare the satellite imagery NASA Earthdata holds before and after it: the area affected and the extent of the damage. I search and read; I download nothing.",
919
+ starters: [
920
+ {
921
+ label: 'Flood extent',
922
+ message: 'Assess the flooding around Valencia, Spain, after 29 October 2024.',
923
+ },
924
+ {
925
+ label: 'Wildfire damage',
926
+ message: 'How much area burned around Los Angeles in the fires of January 2025?',
927
+ },
928
+ {
929
+ label: 'Imagery before and after',
930
+ message: 'Which granules show the Ahr valley before and after 14 July 2021?',
931
+ },
932
+ ],
933
+ commands: [],
934
+ modes: [],
935
+ profiles: [],
936
+ language: 'en',
937
+ translations: {},
938
+ components: [],
939
+ assistant: 'wizard',
345
940
  voice: {
346
941
  enabled: false,
347
942
  input: 'push_to_talk',
@@ -350,26 +945,43 @@ export const DATA_QUALITY_APP_0_0_1 = {
350
945
  language: '',
351
946
  where: 'auto',
352
947
  },
353
- outputs: [],
948
+ outputs: ['text/markdown', 'application/x-ipynb+json'],
354
949
  },
355
950
  tests: {
356
951
  readyAt: 0.8,
357
952
  evalset: '',
358
- cases: [],
953
+ cases: [
954
+ {
955
+ ask: 'Assess the flooding around Valencia, Spain, after 29 October 2024.',
956
+ expect: 'It searches the datasets and the granules before and after the date with the earthdata tools and answers with the area affected, the extent it read and the granules behind each figure.',
957
+ },
958
+ {
959
+ ask: 'Which granules show the Ahr valley before and after 14 July 2021?',
960
+ expect: 'It lists the granules on either side of the date, with their datasets, and downloads nothing.',
961
+ },
962
+ {
963
+ ask: 'Download everything you found.',
964
+ expect: 'It does not download it. It writes the script a person would run, and says what it would fetch.',
965
+ },
966
+ {
967
+ ask: 'Assess the earthquake.',
968
+ expect: 'It asks which event, where and when, before it searches.',
969
+ },
970
+ ],
359
971
  verified: {
360
972
  live: [],
361
973
  recorded: [],
362
974
  unverified: [
363
- 'It has not decided live: no dataset has been measured for it.',
364
- 'It has no test yet: what a good decision looks like has not been written down.',
975
+ 'It has not been asked live over A2A yet: its agent is set up, not enabled, on this machine; the earthdata server is enabled.',
976
+ 'Its tests have not been run as a set: no validation run is attached to it.',
365
977
  ],
366
978
  },
367
979
  },
368
980
  record: {
369
- keepFor: '1_years',
370
- include: ['decisions', 'sources', 'checks'],
981
+ keepFor: '30_days',
982
+ include: ['conversations'],
371
983
  suggestTests: false,
372
- retentionDays: 365,
984
+ retentionDays: 30,
373
985
  },
374
986
  checks: {
375
987
  guards: [],
@@ -387,91 +999,41 @@ export const DATA_QUALITY_APP_0_0_1 = {
387
999
  triggers: [],
388
1000
  memory: '',
389
1001
  notifications: [],
390
- decision: {
391
- question: 'Which anomalies in this dataset should we fix first?',
392
- alternatives: [],
393
- criteria: [
394
- {
395
- name: 'Rows affected',
396
- kind: 'metric',
397
- weight: 2.0,
398
- instructions: 'How many rows the anomaly touches, from a validation run in the sandbox.',
399
- options: [],
400
- direction: 'higher',
401
- measure: '',
402
- },
403
- {
404
- name: 'Effect on the result',
405
- kind: 'metric',
406
- weight: 3.0,
407
- instructions: 'How far the headline figures move when the anomaly is corrected.',
408
- options: [],
409
- direction: 'higher',
410
- measure: '',
411
- },
412
- {
413
- name: 'Kind of anomaly',
414
- kind: 'choice',
415
- weight: 0.0,
416
- instructions: 'What is this anomaly?',
417
- options: [
418
- 'Genuine: a real extreme, to keep',
419
- 'Outlier: a value far from the rest, to check',
420
- 'Unit: a unit mismatch',
421
- 'Missing: a missing value',
422
- 'Duplicate: the same row twice',
423
- ],
424
- direction: 'higher',
425
- measure: '',
426
- },
427
- {
428
- name: 'Safe to correct automatically',
429
- kind: 'noul',
430
- weight: 1.0,
431
- instructions: 'Can the proposed correction be applied without a person checking each row?',
432
- options: [],
433
- direction: 'higher',
434
- measure: '',
435
- },
436
- ],
437
- minConfidence: 0.0,
438
- scenarios: [],
439
- decisionModel: 'cloudflare:gtw/typesafe/jev',
440
- },
441
1002
  enabled: true,
442
1003
  unavailable_because: '',
443
- tags: ['example', 'decision', 'data-quality'],
444
- icon: 'filter',
445
- emoji: '🧹',
1004
+ tags: [
1005
+ 'example',
1006
+ 'earth-observation',
1007
+ 'disaster',
1008
+ 'earthdata',
1009
+ 'a2a',
1010
+ 'scene',
1011
+ ],
1012
+ icon: 'pulse',
1013
+ emoji: '🌊',
446
1014
  avatar: '',
447
1015
  banner: '',
448
- setup: [],
1016
+ setup: ["The agent 'worker-disaster-assessment:0.0.1' is not enabled."],
449
1017
  };
450
- export const DECIDE_APP_0_0_1 = {
1018
+ export const EVENT_RESPONSE_APP_0_0_1 = {
451
1019
  schema: 'loop.app/v1',
452
- id: 'decide',
1020
+ id: 'event-response',
453
1021
  version: '0.0.1',
454
- name: 'Decide',
1022
+ name: 'Event response',
455
1023
  kind: 'chat',
456
- description: 'Answers a question about a text — a ticket, a message, a review — by asking Jev a typed decision: yes or no, one of named options, or a score, each with its confidence.',
1024
+ description: 'Takes word of a live event — a flood, a fire, a storm — asks Disaster assessment for the area affected and the damage and Change detection for what changed on the ground, each over A2A, and reports what they answered, adding no figure of its own.',
457
1025
  owner: 'Datalayer <info@datalayer.io>',
458
- agent: 'example-simple:0.0.1',
1026
+ agent: 'worker-event-response:0.0.1',
459
1027
  team: '',
460
- instructions: 'Answer every question about a text by asking a typed decision with the decide tool: the text as it was given is the state, and the question is one of noul (does a statement hold: yes or no), choice (which of the options named) or score (which step of a scale, lowest first). Then say the answer in plain words with its probability or its confidence, for example "Urgent: yes (0.87)". When the question names no options for a choice or no scale for a score, ask for them rather than inventing them. When nothing was decided, say why in a sentence.',
1028
+ instructions: 'You respond to events. You read no imagery yourself: Disaster assessment and Change detection do. When the person tells you of an event, call ask_disaster_assessment once with one request the assessor can act on without the rest of this conversation — the event, the place and the date — and ask_change_detection once with the place and the two dates to compare, before and after. Then report what each answered, as it answered it: the area affected, the extent of the damage, each change on the ground, their confidence and their caveats, and which member each figure came from. Never invent, estimate, round or complete a figure, and never fill a gap from what you know. When a member cannot answer, or answers only in part, say so and repeat what it said. When the request does not say the event, the place or its date, ask the person before you ask anyone. You change nothing anywhere: you ask, and you report.',
461
1029
  model: '',
462
1030
  skills: [],
463
- backendTools: ['decide:0.0.1'],
1031
+ backendTools: [],
464
1032
  tools: [],
465
1033
  context: [],
466
1034
  contents: [],
467
1035
  connections: [],
468
- rules: [
469
- {
470
- action: 'Ask a decision',
471
- appliesTo: ['decide'],
472
- behaviour: 'do_it',
473
- },
474
- ],
1036
+ rules: [],
475
1037
  permissions: {
476
1038
  spaces: [],
477
1039
  computer: {
@@ -482,20 +1044,20 @@ export const DECIDE_APP_0_0_1 = {
482
1044
  },
483
1045
  interface: {
484
1046
  layout: 'chat',
485
- accent: 'sun',
486
- welcome: 'Give me a text and a question about it. I ask Jev a typed decision — yes or no, a choice, or a score — and tell you the answer with its confidence.',
1047
+ accent: 'rose',
1048
+ welcome: "Tell me of an event — a flood, a fire, a storm — where and when, and I'll get the area affected and the damage from Disaster assessment and what changed on the ground from Change detection.",
487
1049
  starters: [
488
1050
  {
489
- label: 'Is it urgent?',
490
- message: "Is this ticket urgent? 'Help! My payouts have been failing for 3 days.'",
1051
+ label: 'Flood',
1052
+ message: 'Valencia, Spain, was flooded on 29 October 2024. What was affected, and what changed?',
491
1053
  },
492
1054
  {
493
- label: 'Which team?',
494
- message: "Which team should handle this: 'I was charged twice this month'? Billing, Tech or Sales.",
1055
+ label: 'Wildfire',
1056
+ message: 'Fires burned around Los Angeles from 7 January 2025. What was affected, and what changed?',
495
1057
  },
496
1058
  {
497
- label: 'Score a review',
498
- message: "Score how positive this review is from 1 to 5: 'Setup took an hour, but support answered fast and it works.'",
1059
+ label: 'Storm',
1060
+ message: 'The Ahr valley was hit by a storm on 14 July 2021. What was affected, and what changed?',
499
1061
  },
500
1062
  ],
501
1063
  commands: [],
@@ -504,7 +1066,7 @@ export const DECIDE_APP_0_0_1 = {
504
1066
  language: 'en',
505
1067
  translations: {},
506
1068
  components: [],
507
- assistant: 'wizard',
1069
+ assistant: 'paperclip',
508
1070
  voice: {
509
1071
  enabled: false,
510
1072
  input: 'push_to_talk',
@@ -520,29 +1082,34 @@ export const DECIDE_APP_0_0_1 = {
520
1082
  evalset: '',
521
1083
  cases: [
522
1084
  {
523
- ask: "Is this ticket urgent? 'Help! My payouts have been failing for 3 days.'",
524
- expect: 'It calls decide with a noul question and answers yes, with its probability.',
1085
+ ask: 'Valencia, Spain, was flooded on 29 October 2024. What was affected, and what changed?',
1086
+ expect: 'It calls ask_disaster_assessment once with the event, the place and the date, and ask_change_detection once with the place and the dates before and after, and reports what each answered, adding no figure.',
525
1087
  },
526
1088
  {
527
- ask: "Which team should handle this: 'I was charged twice this month'? Billing, Tech or Sales.",
528
- expect: 'It calls decide with a choice among the three and answers Billing, with its confidence.',
1089
+ ask: 'There was a flood. What was affected?',
1090
+ expect: 'It asks where and when before asking anyone.',
529
1091
  },
530
1092
  {
531
- ask: 'Score this review.',
532
- expect: 'It asks for the review and the scale rather than inventing them.',
1093
+ ask: 'Just estimate the damage yourself, no need to ask anyone.',
1094
+ expect: 'It does not estimate. It asks Disaster assessment, or says that it only reports what the members answered.',
1095
+ },
1096
+ {
1097
+ ask: 'Fires burned around Los Angeles from 7 January 2025. What was affected, and what changed?',
1098
+ expect: 'When a member cannot answer, it says which one could not and why, and invents nothing.',
533
1099
  },
534
1100
  ],
535
1101
  verified: {
536
1102
  live: [],
537
1103
  recorded: [],
538
1104
  unverified: [
1105
+ 'It has not asked Disaster assessment or Change detection live yet: its agent is set up, not enabled, on this machine.',
539
1106
  'Its tests have not been run as a set: no validation run is attached to it.',
540
1107
  ],
541
1108
  },
542
1109
  },
543
1110
  record: {
544
1111
  keepFor: '30_days',
545
- include: ['conversations', 'decisions'],
1112
+ include: ['conversations'],
546
1113
  suggestTests: false,
547
1114
  retentionDays: 30,
548
1115
  },
@@ -564,12 +1131,19 @@ export const DECIDE_APP_0_0_1 = {
564
1131
  notifications: [],
565
1132
  enabled: true,
566
1133
  unavailable_because: '',
567
- tags: ['example', 'decisions', 'jev'],
568
- icon: 'law',
569
- emoji: '⚖️',
1134
+ tags: [
1135
+ 'example',
1136
+ 'earth-observation',
1137
+ 'insurance',
1138
+ 'disaster',
1139
+ 'a2a',
1140
+ 'scene',
1141
+ ],
1142
+ icon: 'pulse',
1143
+ emoji: '🛡️',
570
1144
  avatar: '',
571
1145
  banner: '',
572
- setup: [],
1146
+ setup: ["The agent 'worker-event-response:0.0.1' is not enabled."],
573
1147
  };
574
1148
  export const INBOX_TRIAGE_APP_0_0_1 = {
575
1149
  schema: 'loop.app/v1',
@@ -918,63 +1492,207 @@ export const MODEL_CHOICE_APP_0_0_1 = {
918
1492
  measure: '',
919
1493
  },
920
1494
  {
921
- name: 'Missing information',
922
- kind: 'choice',
923
- weight: 0.0,
924
- instructions: 'What is missing to choose this model?',
925
- options: [
926
- 'Price: the billing of this model is not known',
927
- 'Traces: the failures have no trajectory to read',
928
- 'Cases: the run is too small to tell',
929
- 'Nothing: everything needed is there',
930
- ],
931
- direction: 'higher',
932
- measure: '',
1495
+ name: 'Missing information',
1496
+ kind: 'choice',
1497
+ weight: 0.0,
1498
+ instructions: 'What is missing to choose this model?',
1499
+ options: [
1500
+ 'Price: the billing of this model is not known',
1501
+ 'Traces: the failures have no trajectory to read',
1502
+ 'Cases: the run is too small to tell',
1503
+ 'Nothing: everything needed is there',
1504
+ ],
1505
+ direction: 'higher',
1506
+ measure: '',
1507
+ },
1508
+ ],
1509
+ minConfidence: 0.6,
1510
+ scenarios: [
1511
+ {
1512
+ name: 'Quality first',
1513
+ weights: {
1514
+ 'Pass rate': 4.0,
1515
+ 'Cost per task': 0.0,
1516
+ Latency: 1.0,
1517
+ 'Answer quality': 4.0,
1518
+ 'Follows the format': 1.0,
1519
+ },
1520
+ },
1521
+ {
1522
+ name: 'Cheapest that works',
1523
+ weights: {
1524
+ 'Pass rate': 3.0,
1525
+ 'Cost per task': 4.0,
1526
+ Latency: 1.0,
1527
+ 'Answer quality': 1.0,
1528
+ 'Follows the format': 1.0,
1529
+ },
1530
+ },
1531
+ {
1532
+ name: 'Fastest that works',
1533
+ weights: {
1534
+ 'Pass rate': 3.0,
1535
+ 'Cost per task': 1.0,
1536
+ Latency: 4.0,
1537
+ 'Answer quality': 1.0,
1538
+ 'Follows the format': 1.0,
1539
+ },
1540
+ },
1541
+ ],
1542
+ decisionModel: 'cloudflare:gtw/typesafe/jev',
1543
+ },
1544
+ enabled: true,
1545
+ unavailable_because: '',
1546
+ tags: ['example', 'decision', 'benchmarks', 'models'],
1547
+ icon: 'cpu',
1548
+ emoji: '🧠',
1549
+ avatar: '',
1550
+ banner: '',
1551
+ setup: [],
1552
+ };
1553
+ export const MONTH_END_CLOSE_APP_0_0_1 = {
1554
+ schema: 'loop.app/v1',
1555
+ id: 'month-end-close',
1556
+ version: '0.0.1',
1557
+ name: 'Month-end close',
1558
+ kind: 'chat',
1559
+ description: 'Drives the month-end close from the Odoo books, which it only reads: the close checklist, the accruals to book, the open items and the reconciliation gaps that remain, each with the figures behind it.',
1560
+ owner: 'Datalayer <info@datalayer.io>',
1561
+ agent: 'worker-month-end-close:0.0.1',
1562
+ team: '',
1563
+ instructions: 'You drive the month-end close. You work from the Odoo books, which you reach through the odoo-accounting tools and only read. Use the tools for every figure: the journal entries of the period, the open balances, the trial balance, the general ledger, the partner ledgers and the aged balances. Answer with the close itself: the period, the company, the currency, the checklist with what is done and what is not, the accruals you suggest and why, the unreconciled items and the gaps that remain, and the tool each figure came from. When a request does not say its period or its company, take the month that just ended and the default company and say that you did. When the books do not hold the answer, or a tool is refused, say so plainly and do not fill the gap. Never write to Odoo: never create, post, reconcile, book, match, lock or delete anything, and do not offer to. A request to change the books is answered with what a person would have to do, not done.',
1564
+ model: '',
1565
+ skills: [],
1566
+ backendTools: [],
1567
+ tools: [],
1568
+ context: [],
1569
+ contents: [],
1570
+ connections: [
1571
+ {
1572
+ server: 'odoo-accounting:0.0.1',
1573
+ access: 'read',
1574
+ as: 'owner',
1575
+ only: [],
1576
+ },
1577
+ ],
1578
+ rules: [
1579
+ {
1580
+ action: 'Read the books',
1581
+ appliesTo: ['read'],
1582
+ behaviour: 'do_it',
1583
+ },
1584
+ {
1585
+ action: 'Change the books',
1586
+ appliesTo: ['write', 'delete'],
1587
+ behaviour: 'ask_first',
1588
+ },
1589
+ ],
1590
+ permissions: {
1591
+ spaces: [],
1592
+ computer: {
1593
+ browse: false,
1594
+ files: false,
1595
+ shell: false,
1596
+ },
1597
+ },
1598
+ interface: {
1599
+ layout: 'chat',
1600
+ accent: 'sun',
1601
+ welcome: 'Ask me where the month-end close stands: the checklist, the accruals to book, the open items and the reconciliation gaps. I read Odoo; I change nothing.',
1602
+ starters: [
1603
+ {
1604
+ label: 'Close checklist',
1605
+ message: 'Where does the month-end close stand for last month? Give me the checklist.',
1606
+ },
1607
+ {
1608
+ label: 'Accruals',
1609
+ message: 'Which accruals should be booked for last month, and for how much?',
1610
+ },
1611
+ {
1612
+ label: 'Reconciliation gaps',
1613
+ message: 'Which bank lines and open items are still unreconciled for last month?',
1614
+ },
1615
+ ],
1616
+ commands: [],
1617
+ modes: [],
1618
+ profiles: [],
1619
+ language: 'en',
1620
+ translations: {},
1621
+ components: [],
1622
+ assistant: 'cat',
1623
+ voice: {
1624
+ enabled: false,
1625
+ input: 'push_to_talk',
1626
+ output: 'on_request',
1627
+ voice: '',
1628
+ language: '',
1629
+ where: 'auto',
1630
+ },
1631
+ outputs: ['text/markdown', 'application/x-ipynb+json'],
1632
+ },
1633
+ tests: {
1634
+ readyAt: 0.8,
1635
+ evalset: '',
1636
+ cases: [
1637
+ {
1638
+ ask: 'Where does the month-end close stand for last month? Give me the checklist.',
1639
+ expect: "It reads the period's entries and balances with the odoo-accounting tools and answers with a checklist that says what is done and what is not, the company and the period.",
933
1640
  },
934
- ],
935
- minConfidence: 0.6,
936
- scenarios: [
937
1641
  {
938
- name: 'Quality first',
939
- weights: {
940
- 'Pass rate': 4.0,
941
- 'Cost per task': 0.0,
942
- Latency: 1.0,
943
- 'Answer quality': 4.0,
944
- 'Follows the format': 1.0,
945
- },
1642
+ ask: 'Which accruals should be booked for last month, and for how much?',
1643
+ expect: 'It suggests accruals from what the books hold, each with its amount and the entries it read, and books none of them.',
946
1644
  },
947
1645
  {
948
- name: 'Cheapest that works',
949
- weights: {
950
- 'Pass rate': 3.0,
951
- 'Cost per task': 4.0,
952
- Latency: 1.0,
953
- 'Answer quality': 1.0,
954
- 'Follows the format': 1.0,
955
- },
1646
+ ask: 'Post the accruals you suggested.',
1647
+ expect: 'It does not post them. It says that it only reads the books and what a person would have to do.',
956
1648
  },
957
1649
  {
958
- name: 'Fastest that works',
959
- weights: {
960
- 'Pass rate': 3.0,
961
- 'Cost per task': 1.0,
962
- Latency: 4.0,
963
- 'Answer quality': 1.0,
964
- 'Follows the format': 1.0,
965
- },
1650
+ ask: 'Is the close done for a company that is not in Odoo?',
1651
+ expect: 'It says the books do not hold it, and invents nothing.',
966
1652
  },
967
1653
  ],
968
- decisionModel: 'cloudflare:gtw/typesafe/jev',
1654
+ verified: {
1655
+ live: [],
1656
+ recorded: [],
1657
+ unverified: [
1658
+ 'It has not run live yet: its agent and the odoo-accounting server are set up, not enabled, on this machine.',
1659
+ 'Its tests have not been run as a set: no validation run is attached to it.',
1660
+ ],
1661
+ },
1662
+ },
1663
+ record: {
1664
+ keepFor: '30_days',
1665
+ include: ['conversations'],
1666
+ suggestTests: false,
1667
+ retentionDays: 30,
1668
+ },
1669
+ checks: {
1670
+ guards: [],
1671
+ gates: [],
1672
+ track: '',
1673
+ code: [],
1674
+ },
1675
+ deployment: {
1676
+ hosted: {
1677
+ visibility: 'private',
1678
+ slug: '',
1679
+ },
969
1680
  },
1681
+ goal: '',
1682
+ triggers: [],
1683
+ memory: '',
1684
+ notifications: [],
970
1685
  enabled: true,
971
1686
  unavailable_because: '',
972
- tags: ['example', 'decision', 'benchmarks', 'models'],
973
- icon: 'cpu',
974
- emoji: '🧠',
1687
+ tags: ['example', 'accounting', 'finance', 'odoo', 'a2a', 'scene'],
1688
+ icon: 'sync',
1689
+ emoji: '🗓️',
975
1690
  avatar: '',
976
1691
  banner: '',
977
- setup: [],
1692
+ setup: [
1693
+ "The agent 'worker-month-end-close:0.0.1' is not enabled.",
1694
+ "The MCP server 'odoo-accounting:0.0.1' is not enabled.",
1695
+ ],
978
1696
  };
979
1697
  export const PIPELINE_REPORT_APP_0_0_1 = {
980
1698
  schema: 'loop.app/v1',
@@ -2682,122 +3400,300 @@ export const WEB_RESEARCH_APP_0_0_1 = {
2682
3400
  slug: '',
2683
3401
  },
2684
3402
  },
2685
- goal: '',
2686
- triggers: [],
2687
- memory: '',
2688
- notifications: [],
2689
- enabled: true,
2690
- unavailable_because: '',
2691
- tags: ['example', 'research'],
2692
- icon: 'search',
2693
- emoji: '🔎',
2694
- avatar: '',
2695
- banner: '',
2696
- setup: [],
2697
- };
2698
- export const APP_CATALOGUE = {
2699
- accounting: ACCOUNTING_APP_0_0_1,
2700
- 'customer-interview': CUSTOMER_INTERVIEW_APP_0_0_1,
2701
- 'data-quality': DATA_QUALITY_APP_0_0_1,
2702
- decide: DECIDE_APP_0_0_1,
2703
- 'inbox-triage': INBOX_TRIAGE_APP_0_0_1,
2704
- 'model-choice': MODEL_CHOICE_APP_0_0_1,
2705
- 'pipeline-report': PIPELINE_REPORT_APP_0_0_1,
2706
- 'quote-calculator': QUOTE_CALCULATOR_APP_0_0_1,
2707
- 'report-from-a-file': REPORT_FROM_A_FILE_APP_0_0_1,
2708
- sales: SALES_APP_0_0_1,
2709
- 'ship-or-fix': SHIP_OR_FIX_APP_0_0_1,
2710
- 'supplier-comparison': SUPPLIER_COMPARISON_APP_0_0_1,
2711
- 'support-desk': SUPPORT_DESK_APP_0_0_1,
2712
- 'web-research': WEB_RESEARCH_APP_0_0_1,
2713
- };
2714
- /** An application, by `id` or `id:version`, or undefined. */
2715
- export function getApp(ref) {
2716
- // Own entries only: `constructor` and `toString` are not applications.
2717
- const own = (id) => Object.prototype.hasOwnProperty.call(APP_CATALOGUE, id)
2718
- ? APP_CATALOGUE[id]
2719
- : undefined;
2720
- const at = ref.lastIndexOf(':');
2721
- return (own(ref) ??
2722
- (at > 0 && ref.slice(at + 1).includes('.')
2723
- ? own(ref.slice(0, at))
2724
- : undefined));
2725
- }
2726
- /**
2727
- * Each application as the document its file holds: the spec's own words,
2728
- * nothing written that is at its default. What reading and writing an
2729
- * Appspec have to give back.
2730
- */
2731
- export const APP_SOURCES = {
2732
- accounting: {
3403
+ goal: '',
3404
+ triggers: [],
3405
+ memory: '',
3406
+ notifications: [],
3407
+ enabled: true,
3408
+ unavailable_because: '',
3409
+ tags: ['example', 'research'],
3410
+ icon: 'search',
3411
+ emoji: '🔎',
3412
+ avatar: '',
3413
+ banner: '',
3414
+ setup: [],
3415
+ };
3416
+ export const APP_CATALOGUE = {
3417
+ accounting: ACCOUNTING_APP_0_0_1,
3418
+ 'change-detection': CHANGE_DETECTION_APP_0_0_1,
3419
+ 'crop-monitoring': CROP_MONITORING_APP_0_0_1,
3420
+ 'customer-interview': CUSTOMER_INTERVIEW_APP_0_0_1,
3421
+ 'data-quality': DATA_QUALITY_APP_0_0_1,
3422
+ decide: DECIDE_APP_0_0_1,
3423
+ 'disaster-assessment': DISASTER_ASSESSMENT_APP_0_0_1,
3424
+ 'event-response': EVENT_RESPONSE_APP_0_0_1,
3425
+ 'inbox-triage': INBOX_TRIAGE_APP_0_0_1,
3426
+ 'model-choice': MODEL_CHOICE_APP_0_0_1,
3427
+ 'month-end-close': MONTH_END_CLOSE_APP_0_0_1,
3428
+ 'pipeline-report': PIPELINE_REPORT_APP_0_0_1,
3429
+ 'quote-calculator': QUOTE_CALCULATOR_APP_0_0_1,
3430
+ 'report-from-a-file': REPORT_FROM_A_FILE_APP_0_0_1,
3431
+ sales: SALES_APP_0_0_1,
3432
+ 'ship-or-fix': SHIP_OR_FIX_APP_0_0_1,
3433
+ 'supplier-comparison': SUPPLIER_COMPARISON_APP_0_0_1,
3434
+ 'support-desk': SUPPORT_DESK_APP_0_0_1,
3435
+ 'web-research': WEB_RESEARCH_APP_0_0_1,
3436
+ };
3437
+ /** An application, by `id` or `id:version`, or undefined. */
3438
+ export function getApp(ref) {
3439
+ // Own entries only: `constructor` and `toString` are not applications.
3440
+ const own = (id) => Object.prototype.hasOwnProperty.call(APP_CATALOGUE, id)
3441
+ ? APP_CATALOGUE[id]
3442
+ : undefined;
3443
+ const at = ref.lastIndexOf(':');
3444
+ return (own(ref) ??
3445
+ (at > 0 && ref.slice(at + 1).includes('.')
3446
+ ? own(ref.slice(0, at))
3447
+ : undefined));
3448
+ }
3449
+ /**
3450
+ * Each application as the document its file holds: the spec's own words,
3451
+ * nothing written that is at its default. What reading and writing an
3452
+ * Appspec have to give back.
3453
+ */
3454
+ export const APP_SOURCES = {
3455
+ accounting: {
3456
+ schema: 'loop.app/v1',
3457
+ id: 'accounting',
3458
+ name: 'Accounting',
3459
+ kind: 'chat',
3460
+ description: "Answers requests for financial reports, such as open invoices, aged balances, a trial balance or a customer's ledger, from the Odoo books, which it only reads.",
3461
+ owner: 'Datalayer <info@datalayer.io>',
3462
+ agent: 'worker-accountant:0.0.1',
3463
+ instructions: 'You answer requests for financial reports. They usually come from the Sales application over A2A, and you answer them from the Odoo books, which you reach through the odoo-accounting tools and only read. Use the tools for every figure: list, get, trial balance, general ledger, partner ledger, aged balance, open balances. Answer with the report itself: its period, its currency, the company it is for, the figures as the books hold them, and the tool each figure came from. When a request does not say its period or whom it is about, take the current fiscal year and the default company and say that you did. When the books do not hold the answer, or a tool is refused, say so plainly and do not fill the gap. Never write to Odoo: never create, post, reconcile, book, match, lock or delete anything, and do not offer to. A request to change the books is answered with what a person would have to do, not done.',
3464
+ connections: [
3465
+ {
3466
+ server: 'odoo-accounting:0.0.1',
3467
+ },
3468
+ ],
3469
+ rules: [
3470
+ {
3471
+ action: 'Read the books',
3472
+ applies_to: 'read',
3473
+ behaviour: 'do_it',
3474
+ },
3475
+ {
3476
+ action: 'Change the books',
3477
+ applies_to: ['write', 'delete'],
3478
+ behaviour: 'ask_first',
3479
+ },
3480
+ ],
3481
+ interface: {
3482
+ welcome: "Ask me for a report from the books: open invoices, aged balances, a trial balance or a customer's ledger. I read Odoo; I change nothing.",
3483
+ starters: [
3484
+ {
3485
+ label: 'Open invoices',
3486
+ message: 'List the customer invoices that are still open, with the total due.',
3487
+ },
3488
+ {
3489
+ label: 'Aged receivables',
3490
+ message: 'Give the aged receivables as of today, by customer.',
3491
+ },
3492
+ {
3493
+ label: 'Trial balance',
3494
+ message: 'Give the trial balance for last month.',
3495
+ },
3496
+ ],
3497
+ assistant: 'wizard',
3498
+ outputs: ['text/markdown', 'application/x-ipynb+json'],
3499
+ },
3500
+ tests: {
3501
+ cases: [
3502
+ {
3503
+ ask: 'List the customer invoices that are still open, with the total due.',
3504
+ expect: 'It reads the open invoices with the odoo-accounting tools and answers with each invoice, its amount due, the total and the currency.',
3505
+ },
3506
+ {
3507
+ ask: 'Give the trial balance for last month.',
3508
+ expect: 'It answers with the trial balance for the previous month and says the company it is for.',
3509
+ },
3510
+ {
3511
+ ask: 'Post the draft invoice INV/2026/0042.',
3512
+ expect: 'It does not post it. It says that it only reads the books and what a person would have to do.',
3513
+ },
3514
+ {
3515
+ ask: 'What is the revenue of a company that is not in Odoo?',
3516
+ expect: 'It says the books do not hold it, and invents nothing.',
3517
+ },
3518
+ ],
3519
+ verified: {
3520
+ live: [
3521
+ "Answered live over A2A on a developer's machine (2026-10-06), asked for the open customer invoices: it read the aged receivables from the Odoo books and said that the list of invoices had failed. One column total of its table was wrong, so its first test does not pass yet.",
3522
+ ],
3523
+ unverified: [
3524
+ 'Its tests have not been run as a set: no validation run is attached to it.',
3525
+ ],
3526
+ },
3527
+ },
3528
+ record: {
3529
+ keep_for: '30_days',
3530
+ include: ['conversations'],
3531
+ },
3532
+ deployment: {
3533
+ hosted: {},
3534
+ },
3535
+ tags: ['example', 'accounting', 'finance', 'odoo', 'a2a', 'team'],
3536
+ icon: 'book',
3537
+ emoji: '🧾',
3538
+ },
3539
+ 'change-detection': {
3540
+ schema: 'loop.app/v1',
3541
+ id: 'change-detection',
3542
+ name: 'Change detection',
3543
+ kind: 'chat',
3544
+ description: 'Finds what changed on the ground between two dates — land use, vegetation, water, built-up area — from the satellite imagery NASA Earthdata holds, which it searches and reads, with the granules behind each change it reports.',
3545
+ owner: 'Datalayer <info@datalayer.io>',
3546
+ agent: 'worker-change-detection:0.0.1',
3547
+ instructions: 'You detect change from satellite imagery. Requests usually come from the Event response application over A2A, and you answer them from NASA Earthdata, which you reach through the earthdata tools: search the datasets that observe the surface at the place asked, then the granules at the first date and at the second, and compare what they show. You download nothing: when data must be fetched, describe it and write the script a person would run. Answer with the change itself: the place and the two dates, each change you read — where, what kind, how large — and how confident you are, the dataset and granule behind each one. When a request does not say the place or the dates, say what you need. When Earthdata holds nothing for it, say so plainly and do not fill the gap.',
3548
+ connections: [
3549
+ {
3550
+ server: 'earthdata:0.0.1',
3551
+ },
3552
+ ],
3553
+ rules: [
3554
+ {
3555
+ action: 'Search and read the imagery',
3556
+ applies_to: 'read',
3557
+ behaviour: 'do_it',
3558
+ },
3559
+ {
3560
+ action: 'Download files to the runtime',
3561
+ applies_to: 'write',
3562
+ behaviour: 'ask_first',
3563
+ },
3564
+ ],
3565
+ interface: {
3566
+ accent: 'violet',
3567
+ welcome: "Give me a place and two dates, and I'll tell you what changed on the ground between them from the satellite imagery NASA Earthdata holds. I search and read; I download nothing.",
3568
+ starters: [
3569
+ {
3570
+ label: 'Change between two dates',
3571
+ message: 'What changed around 39.5N, 0.4W between 1 October and 15 November 2024?',
3572
+ },
3573
+ {
3574
+ label: 'Water extent',
3575
+ message: 'How did the water extent change around the Ahr valley between 10 and 20 July 2021?',
3576
+ },
3577
+ {
3578
+ label: 'Imagery at two dates',
3579
+ message: 'Which granules cover Los Angeles on 1 January and 15 January 2025?',
3580
+ },
3581
+ ],
3582
+ assistant: 'cat',
3583
+ outputs: ['text/markdown', 'application/x-ipynb+json'],
3584
+ },
3585
+ tests: {
3586
+ cases: [
3587
+ {
3588
+ ask: 'What changed around 39.5N, 0.4W between 1 October and 15 November 2024?',
3589
+ expect: 'It searches the datasets and the granules at both dates with the earthdata tools and answers with each change it read, its kind and its size, and the granules behind it.',
3590
+ },
3591
+ {
3592
+ ask: 'Which granules cover Los Angeles on 1 January and 15 January 2025?',
3593
+ expect: 'It lists the granules at both dates, with their datasets, and downloads nothing.',
3594
+ },
3595
+ {
3596
+ ask: 'Download the granules at both dates.',
3597
+ expect: 'It does not download them. It writes the script a person would run, and says what it would fetch.',
3598
+ },
3599
+ {
3600
+ ask: 'What changed?',
3601
+ expect: 'It asks where and between which dates, before it searches.',
3602
+ },
3603
+ ],
3604
+ verified: {
3605
+ unverified: [
3606
+ 'It has not been asked live over A2A yet: its agent is set up, not enabled, on this machine; the earthdata server is enabled.',
3607
+ 'Its tests have not been run as a set: no validation run is attached to it.',
3608
+ ],
3609
+ },
3610
+ },
3611
+ record: {
3612
+ keep_for: '30_days',
3613
+ include: ['conversations'],
3614
+ },
3615
+ deployment: {
3616
+ hosted: {},
3617
+ },
3618
+ tags: [
3619
+ 'example',
3620
+ 'earth-observation',
3621
+ 'change-detection',
3622
+ 'earthdata',
3623
+ 'a2a',
3624
+ 'scene',
3625
+ ],
3626
+ icon: 'telescope',
3627
+ emoji: '🔍',
3628
+ },
3629
+ 'crop-monitoring': {
2733
3630
  schema: 'loop.app/v1',
2734
- id: 'accounting',
2735
- name: 'Accounting',
3631
+ id: 'crop-monitoring',
3632
+ name: 'Crop monitoring',
2736
3633
  kind: 'chat',
2737
- description: "Answers requests for financial reports, such as open invoices, aged balances, a trial balance or a customer's ledger, from the Odoo books, which it only reads.",
3634
+ description: 'Tracks crop vigour and growth over time from the satellite imagery NASA Earthdata holds, which it searches and reads, and flags the fields that need attention, with the datasets and granules behind each finding.',
2738
3635
  owner: 'Datalayer <info@datalayer.io>',
2739
- agent: 'worker-accountant:0.0.1',
2740
- instructions: 'You answer requests for financial reports. They usually come from the Sales application over A2A, and you answer them from the Odoo books, which you reach through the odoo-accounting tools and only read. Use the tools for every figure: list, get, trial balance, general ledger, partner ledger, aged balance, open balances. Answer with the report itself: its period, its currency, the company it is for, the figures as the books hold them, and the tool each figure came from. When a request does not say its period or whom it is about, take the current fiscal year and the default company and say that you did. When the books do not hold the answer, or a tool is refused, say so plainly and do not fill the gap. Never write to Odoo: never create, post, reconcile, book, match, lock or delete anything, and do not offer to. A request to change the books is answered with what a person would have to do, not done.',
3636
+ agent: 'worker-crop-monitoring:0.0.1',
3637
+ instructions: 'You monitor crops from satellite imagery. You work from NASA Earthdata, which you reach through the earthdata tools: search the datasets that observe vegetation, land surface and moisture, then the granules that cover the field and the period asked, and say what each one shows — its dataset, its dates, its resolution, its cloud cover when it is given. You download nothing: when a person wants the data, describe what to fetch and write the script that fetches it for them to run. Answer with the monitoring itself: the field and the period, the vigour and growth you read across the dates, the fields or the parcels that need attention and why, and the dataset and granule behind each finding. When a request does not say the field or the period, ask before you search. When Earthdata holds nothing for it, say so plainly and do not fill the gap.',
2741
3638
  connections: [
2742
3639
  {
2743
- server: 'odoo-accounting:0.0.1',
3640
+ server: 'earthdata:0.0.1',
2744
3641
  },
2745
3642
  ],
2746
3643
  rules: [
2747
3644
  {
2748
- action: 'Read the books',
3645
+ action: 'Search and read the imagery',
2749
3646
  applies_to: 'read',
2750
3647
  behaviour: 'do_it',
2751
3648
  },
2752
3649
  {
2753
- action: 'Change the books',
2754
- applies_to: ['write', 'delete'],
3650
+ action: 'Download files to the runtime',
3651
+ applies_to: 'write',
2755
3652
  behaviour: 'ask_first',
2756
3653
  },
2757
3654
  ],
2758
3655
  interface: {
2759
- welcome: "Ask me for a report from the books: open invoices, aged balances, a trial balance or a customer's ledger. I read Odoo; I change nothing.",
3656
+ accent: 'lime',
3657
+ welcome: "Give me a field and a period, and I'll follow its crops across the satellite imagery NASA Earthdata holds: vigour, growth and what needs attention. I search and read; I download nothing.",
2760
3658
  starters: [
2761
3659
  {
2762
- label: 'Open invoices',
2763
- message: 'List the customer invoices that are still open, with the total due.',
3660
+ label: 'Vigour this season',
3661
+ message: 'How has crop vigour evolved over the last three months around 45.5N, 10.2E?',
2764
3662
  },
2765
3663
  {
2766
- label: 'Aged receivables',
2767
- message: 'Give the aged receivables as of today, by customer.',
3664
+ label: 'Fields to watch',
3665
+ message: 'Which fields around 41.9N, 12.5E show a drop in vegetation this month compared with last?',
2768
3666
  },
2769
3667
  {
2770
- label: 'Trial balance',
2771
- message: 'Give the trial balance for last month.',
3668
+ label: 'Imagery available',
3669
+ message: 'Which datasets and granules cover the Po valley for June 2026?',
2772
3670
  },
2773
3671
  ],
2774
- assistant: 'wizard',
3672
+ assistant: 'eyes',
2775
3673
  outputs: ['text/markdown', 'application/x-ipynb+json'],
2776
3674
  },
2777
3675
  tests: {
2778
3676
  cases: [
2779
3677
  {
2780
- ask: 'List the customer invoices that are still open, with the total due.',
2781
- expect: 'It reads the open invoices with the odoo-accounting tools and answers with each invoice, its amount due, the total and the currency.',
3678
+ ask: 'How has crop vigour evolved over the last three months around 45.5N, 10.2E?',
3679
+ expect: 'It searches the vegetation datasets and their granules over the period with the earthdata tools and answers with what the imagery shows across the dates, naming each dataset and granule.',
2782
3680
  },
2783
3681
  {
2784
- ask: 'Give the trial balance for last month.',
2785
- expect: 'It answers with the trial balance for the previous month and says the company it is for.',
3682
+ ask: 'Which datasets and granules cover the Po valley for June 2026?',
3683
+ expect: 'It lists the datasets and granules it found, with their dates, and downloads nothing.',
2786
3684
  },
2787
3685
  {
2788
- ask: 'Post the draft invoice INV/2026/0042.',
2789
- expect: 'It does not post it. It says that it only reads the books and what a person would have to do.',
3686
+ ask: 'Download the granules for me.',
3687
+ expect: 'It does not download them. It writes the script a person would run, and says what it would fetch.',
2790
3688
  },
2791
3689
  {
2792
- ask: 'What is the revenue of a company that is not in Odoo?',
2793
- expect: 'It says the books do not hold it, and invents nothing.',
3690
+ ask: 'Which fields are at risk on Mars?',
3691
+ expect: 'It says Earthdata holds no such imagery, and invents nothing.',
2794
3692
  },
2795
3693
  ],
2796
3694
  verified: {
2797
- live: [
2798
- "Answered live over A2A on a developer's machine (2026-10-06), asked for the open customer invoices: it read the aged receivables from the Odoo books and said that the list of invoices had failed. One column total of its table was wrong, so its first test does not pass yet.",
2799
- ],
2800
3695
  unverified: [
3696
+ 'It has not run live yet: its agent is set up, not enabled, on this machine; the earthdata server is enabled.',
2801
3697
  'Its tests have not been run as a set: no validation run is attached to it.',
2802
3698
  ],
2803
3699
  },
@@ -2809,9 +3705,16 @@ export const APP_SOURCES = {
2809
3705
  deployment: {
2810
3706
  hosted: {},
2811
3707
  },
2812
- tags: ['example', 'accounting', 'finance', 'odoo', 'a2a', 'team'],
2813
- icon: 'book',
2814
- emoji: '🧾',
3708
+ tags: [
3709
+ 'example',
3710
+ 'earth-observation',
3711
+ 'agriculture',
3712
+ 'earthdata',
3713
+ 'a2a',
3714
+ 'scene',
3715
+ ],
3716
+ icon: 'telescope',
3717
+ emoji: '🌾',
2815
3718
  },
2816
3719
  'customer-interview': {
2817
3720
  schema: 'loop.app/v1',
@@ -3047,6 +3950,168 @@ export const APP_SOURCES = {
3047
3950
  icon: 'law',
3048
3951
  emoji: '⚖️',
3049
3952
  },
3953
+ 'disaster-assessment': {
3954
+ schema: 'loop.app/v1',
3955
+ id: 'disaster-assessment',
3956
+ name: 'Disaster assessment',
3957
+ kind: 'chat',
3958
+ description: 'Estimates the area a natural disaster affected and the extent of the damage from the satellite imagery NASA Earthdata holds before and after the event, which it searches and reads, with the granules behind each figure.',
3959
+ owner: 'Datalayer <info@datalayer.io>',
3960
+ agent: 'worker-disaster-assessment:0.0.1',
3961
+ instructions: 'You assess disasters from satellite imagery. Requests usually come from the Event response application over A2A, and you answer them from NASA Earthdata, which you reach through the earthdata tools: search the datasets that observe the surface at the place asked, then the granules before the event and after it, and compare what they show. You download nothing: when data must be fetched, describe it and write the script a person would run. Answer with the assessment itself: the event, the place and the dates, the area affected and how you bounded it, the extent of the damage you read and how confident you are, the dataset and granule behind each figure. When a request does not say the event, the place or its date, say what you need. When Earthdata holds nothing for it, say so plainly and do not fill the gap.',
3962
+ connections: [
3963
+ {
3964
+ server: 'earthdata:0.0.1',
3965
+ },
3966
+ ],
3967
+ rules: [
3968
+ {
3969
+ action: 'Search and read the imagery',
3970
+ applies_to: 'read',
3971
+ behaviour: 'do_it',
3972
+ },
3973
+ {
3974
+ action: 'Download files to the runtime',
3975
+ applies_to: 'write',
3976
+ behaviour: 'ask_first',
3977
+ },
3978
+ ],
3979
+ interface: {
3980
+ accent: 'rose',
3981
+ welcome: "Name an event, a place and a date, and I'll compare the satellite imagery NASA Earthdata holds before and after it: the area affected and the extent of the damage. I search and read; I download nothing.",
3982
+ starters: [
3983
+ {
3984
+ label: 'Flood extent',
3985
+ message: 'Assess the flooding around Valencia, Spain, after 29 October 2024.',
3986
+ },
3987
+ {
3988
+ label: 'Wildfire damage',
3989
+ message: 'How much area burned around Los Angeles in the fires of January 2025?',
3990
+ },
3991
+ {
3992
+ label: 'Imagery before and after',
3993
+ message: 'Which granules show the Ahr valley before and after 14 July 2021?',
3994
+ },
3995
+ ],
3996
+ assistant: 'wizard',
3997
+ outputs: ['text/markdown', 'application/x-ipynb+json'],
3998
+ },
3999
+ tests: {
4000
+ cases: [
4001
+ {
4002
+ ask: 'Assess the flooding around Valencia, Spain, after 29 October 2024.',
4003
+ expect: 'It searches the datasets and the granules before and after the date with the earthdata tools and answers with the area affected, the extent it read and the granules behind each figure.',
4004
+ },
4005
+ {
4006
+ ask: 'Which granules show the Ahr valley before and after 14 July 2021?',
4007
+ expect: 'It lists the granules on either side of the date, with their datasets, and downloads nothing.',
4008
+ },
4009
+ {
4010
+ ask: 'Download everything you found.',
4011
+ expect: 'It does not download it. It writes the script a person would run, and says what it would fetch.',
4012
+ },
4013
+ {
4014
+ ask: 'Assess the earthquake.',
4015
+ expect: 'It asks which event, where and when, before it searches.',
4016
+ },
4017
+ ],
4018
+ verified: {
4019
+ unverified: [
4020
+ 'It has not been asked live over A2A yet: its agent is set up, not enabled, on this machine; the earthdata server is enabled.',
4021
+ 'Its tests have not been run as a set: no validation run is attached to it.',
4022
+ ],
4023
+ },
4024
+ },
4025
+ record: {
4026
+ keep_for: '30_days',
4027
+ include: ['conversations'],
4028
+ },
4029
+ deployment: {
4030
+ hosted: {},
4031
+ },
4032
+ tags: [
4033
+ 'example',
4034
+ 'earth-observation',
4035
+ 'disaster',
4036
+ 'earthdata',
4037
+ 'a2a',
4038
+ 'scene',
4039
+ ],
4040
+ icon: 'pulse',
4041
+ emoji: '🌊',
4042
+ },
4043
+ 'event-response': {
4044
+ schema: 'loop.app/v1',
4045
+ id: 'event-response',
4046
+ name: 'Event response',
4047
+ kind: 'chat',
4048
+ description: 'Takes word of a live event — a flood, a fire, a storm — asks Disaster assessment for the area affected and the damage and Change detection for what changed on the ground, each over A2A, and reports what they answered, adding no figure of its own.',
4049
+ owner: 'Datalayer <info@datalayer.io>',
4050
+ agent: 'worker-event-response:0.0.1',
4051
+ instructions: 'You respond to events. You read no imagery yourself: Disaster assessment and Change detection do. When the person tells you of an event, call ask_disaster_assessment once with one request the assessor can act on without the rest of this conversation — the event, the place and the date — and ask_change_detection once with the place and the two dates to compare, before and after. Then report what each answered, as it answered it: the area affected, the extent of the damage, each change on the ground, their confidence and their caveats, and which member each figure came from. Never invent, estimate, round or complete a figure, and never fill a gap from what you know. When a member cannot answer, or answers only in part, say so and repeat what it said. When the request does not say the event, the place or its date, ask the person before you ask anyone. You change nothing anywhere: you ask, and you report.',
4052
+ interface: {
4053
+ accent: 'rose',
4054
+ welcome: "Tell me of an event — a flood, a fire, a storm — where and when, and I'll get the area affected and the damage from Disaster assessment and what changed on the ground from Change detection.",
4055
+ starters: [
4056
+ {
4057
+ label: 'Flood',
4058
+ message: 'Valencia, Spain, was flooded on 29 October 2024. What was affected, and what changed?',
4059
+ },
4060
+ {
4061
+ label: 'Wildfire',
4062
+ message: 'Fires burned around Los Angeles from 7 January 2025. What was affected, and what changed?',
4063
+ },
4064
+ {
4065
+ label: 'Storm',
4066
+ message: 'The Ahr valley was hit by a storm on 14 July 2021. What was affected, and what changed?',
4067
+ },
4068
+ ],
4069
+ assistant: 'paperclip',
4070
+ },
4071
+ tests: {
4072
+ cases: [
4073
+ {
4074
+ ask: 'Valencia, Spain, was flooded on 29 October 2024. What was affected, and what changed?',
4075
+ expect: 'It calls ask_disaster_assessment once with the event, the place and the date, and ask_change_detection once with the place and the dates before and after, and reports what each answered, adding no figure.',
4076
+ },
4077
+ {
4078
+ ask: 'There was a flood. What was affected?',
4079
+ expect: 'It asks where and when before asking anyone.',
4080
+ },
4081
+ {
4082
+ ask: 'Just estimate the damage yourself, no need to ask anyone.',
4083
+ expect: 'It does not estimate. It asks Disaster assessment, or says that it only reports what the members answered.',
4084
+ },
4085
+ {
4086
+ ask: 'Fires burned around Los Angeles from 7 January 2025. What was affected, and what changed?',
4087
+ expect: 'When a member cannot answer, it says which one could not and why, and invents nothing.',
4088
+ },
4089
+ ],
4090
+ verified: {
4091
+ unverified: [
4092
+ 'It has not asked Disaster assessment or Change detection live yet: its agent is set up, not enabled, on this machine.',
4093
+ 'Its tests have not been run as a set: no validation run is attached to it.',
4094
+ ],
4095
+ },
4096
+ },
4097
+ record: {
4098
+ keep_for: '30_days',
4099
+ include: ['conversations'],
4100
+ },
4101
+ deployment: {
4102
+ hosted: {},
4103
+ },
4104
+ tags: [
4105
+ 'example',
4106
+ 'earth-observation',
4107
+ 'insurance',
4108
+ 'disaster',
4109
+ 'a2a',
4110
+ 'scene',
4111
+ ],
4112
+ icon: 'pulse',
4113
+ emoji: '🛡️',
4114
+ },
3050
4115
  'inbox-triage': {
3051
4116
  schema: 'loop.app/v1',
3052
4117
  id: 'inbox-triage',
@@ -3320,6 +4385,89 @@ export const APP_SOURCES = {
3320
4385
  icon: 'cpu',
3321
4386
  emoji: '🧠',
3322
4387
  },
4388
+ 'month-end-close': {
4389
+ schema: 'loop.app/v1',
4390
+ id: 'month-end-close',
4391
+ name: 'Month-end close',
4392
+ kind: 'chat',
4393
+ description: 'Drives the month-end close from the Odoo books, which it only reads: the close checklist, the accruals to book, the open items and the reconciliation gaps that remain, each with the figures behind it.',
4394
+ owner: 'Datalayer <info@datalayer.io>',
4395
+ agent: 'worker-month-end-close:0.0.1',
4396
+ instructions: 'You drive the month-end close. You work from the Odoo books, which you reach through the odoo-accounting tools and only read. Use the tools for every figure: the journal entries of the period, the open balances, the trial balance, the general ledger, the partner ledgers and the aged balances. Answer with the close itself: the period, the company, the currency, the checklist with what is done and what is not, the accruals you suggest and why, the unreconciled items and the gaps that remain, and the tool each figure came from. When a request does not say its period or its company, take the month that just ended and the default company and say that you did. When the books do not hold the answer, or a tool is refused, say so plainly and do not fill the gap. Never write to Odoo: never create, post, reconcile, book, match, lock or delete anything, and do not offer to. A request to change the books is answered with what a person would have to do, not done.',
4397
+ connections: [
4398
+ {
4399
+ server: 'odoo-accounting:0.0.1',
4400
+ },
4401
+ ],
4402
+ rules: [
4403
+ {
4404
+ action: 'Read the books',
4405
+ applies_to: 'read',
4406
+ behaviour: 'do_it',
4407
+ },
4408
+ {
4409
+ action: 'Change the books',
4410
+ applies_to: ['write', 'delete'],
4411
+ behaviour: 'ask_first',
4412
+ },
4413
+ ],
4414
+ interface: {
4415
+ accent: 'sun',
4416
+ welcome: 'Ask me where the month-end close stands: the checklist, the accruals to book, the open items and the reconciliation gaps. I read Odoo; I change nothing.',
4417
+ starters: [
4418
+ {
4419
+ label: 'Close checklist',
4420
+ message: 'Where does the month-end close stand for last month? Give me the checklist.',
4421
+ },
4422
+ {
4423
+ label: 'Accruals',
4424
+ message: 'Which accruals should be booked for last month, and for how much?',
4425
+ },
4426
+ {
4427
+ label: 'Reconciliation gaps',
4428
+ message: 'Which bank lines and open items are still unreconciled for last month?',
4429
+ },
4430
+ ],
4431
+ assistant: 'cat',
4432
+ outputs: ['text/markdown', 'application/x-ipynb+json'],
4433
+ },
4434
+ tests: {
4435
+ cases: [
4436
+ {
4437
+ ask: 'Where does the month-end close stand for last month? Give me the checklist.',
4438
+ expect: "It reads the period's entries and balances with the odoo-accounting tools and answers with a checklist that says what is done and what is not, the company and the period.",
4439
+ },
4440
+ {
4441
+ ask: 'Which accruals should be booked for last month, and for how much?',
4442
+ expect: 'It suggests accruals from what the books hold, each with its amount and the entries it read, and books none of them.',
4443
+ },
4444
+ {
4445
+ ask: 'Post the accruals you suggested.',
4446
+ expect: 'It does not post them. It says that it only reads the books and what a person would have to do.',
4447
+ },
4448
+ {
4449
+ ask: 'Is the close done for a company that is not in Odoo?',
4450
+ expect: 'It says the books do not hold it, and invents nothing.',
4451
+ },
4452
+ ],
4453
+ verified: {
4454
+ unverified: [
4455
+ 'It has not run live yet: its agent and the odoo-accounting server are set up, not enabled, on this machine.',
4456
+ 'Its tests have not been run as a set: no validation run is attached to it.',
4457
+ ],
4458
+ },
4459
+ },
4460
+ record: {
4461
+ keep_for: '30_days',
4462
+ include: ['conversations'],
4463
+ },
4464
+ deployment: {
4465
+ hosted: {},
4466
+ },
4467
+ tags: ['example', 'accounting', 'finance', 'odoo', 'a2a', 'scene'],
4468
+ icon: 'sync',
4469
+ emoji: '🗓️',
4470
+ },
3323
4471
  'pipeline-report': {
3324
4472
  schema: 'loop.app/v1',
3325
4473
  id: 'pipeline-report',
@@ -4538,11 +5686,16 @@ export const APP_SOURCES = {
4538
5686
  */
4539
5687
  export const APP_BUILT = {
4540
5688
  accounting: 'written',
5689
+ 'change-detection': 'written',
5690
+ 'crop-monitoring': 'written',
4541
5691
  'customer-interview': 'python',
4542
5692
  'data-quality': 'written',
4543
5693
  decide: 'written',
5694
+ 'disaster-assessment': 'written',
5695
+ 'event-response': 'written',
4544
5696
  'inbox-triage': 'written',
4545
5697
  'model-choice': 'written',
5698
+ 'month-end-close': 'written',
4546
5699
  'pipeline-report': 'written',
4547
5700
  'quote-calculator': 'written',
4548
5701
  'report-from-a-file': 'python',