@datalayer/agent-runtimes 1.5.89 → 1.5.91
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.
- package/README.md +29 -297
- package/dist-embed/datalayer-app-main.js +40 -36
- package/lib/apps/embed/element.js +20 -5
- package/lib/apps/embed/visitorApp.d.ts +23 -45
- package/lib/apps/embed/visitorApp.js +65 -13
- package/lib/components/teams/A2ATeamGraph.d.ts +58 -152
- package/lib/components/teams/A2ATeamGraph.js +169 -410
- package/lib/components/teams/SceneTranscript.d.ts +5 -1
- package/lib/components/teams/SceneTranscript.js +2 -2
- package/lib/components/teams/SceneView.d.ts +17 -5
- package/lib/components/teams/SceneView.js +7 -3
- package/lib/components/teams/a2aTeamFlow.d.ts +16 -7
- package/lib/components/teams/a2aTeamFlow.js +17 -14
- package/lib/components/teams/backdrops/catalogue.d.ts +35 -0
- package/lib/components/teams/backdrops/catalogue.js +34 -0
- package/lib/components/teams/backdrops/edges.d.ts +20 -0
- package/lib/components/teams/backdrops/edges.js +29 -0
- package/lib/components/teams/backdrops/faces.d.ts +24 -0
- package/lib/components/teams/backdrops/faces.js +39 -0
- package/lib/components/teams/backdrops/halo.d.ts +14 -0
- package/lib/components/teams/backdrops/halo.js +5 -0
- package/lib/components/teams/backdrops/hero.d.ts +14 -0
- package/lib/components/teams/backdrops/hero.js +178 -0
- package/lib/components/teams/backdrops/index.d.ts +22 -0
- package/lib/components/teams/backdrops/index.js +38 -0
- package/lib/components/teams/backdrops/nodes.d.ts +123 -0
- package/lib/components/teams/backdrops/nodes.js +265 -0
- package/lib/components/teams/backdrops/plain.d.ts +14 -0
- package/lib/components/teams/backdrops/plain.js +116 -0
- package/lib/components/teams/backdrops/studio.d.ts +17 -0
- package/lib/components/teams/backdrops/studio.js +122 -0
- package/lib/components/teams/backdrops/theme.d.ts +14 -0
- package/lib/components/teams/backdrops/theme.js +19 -0
- package/lib/components/teams/backdrops/types.d.ts +214 -0
- package/lib/components/teams/backdrops/types.js +5 -0
- package/lib/components/teams/index.d.ts +2 -0
- package/lib/components/teams/index.js +2 -0
- package/lib/components/teams/sceneTranscriptRules.d.ts +32 -1
- package/lib/components/teams/sceneTranscriptRules.js +104 -2
- package/lib/components/teams/transcript/RecordingLabel.d.ts +17 -0
- package/lib/components/teams/transcript/RecordingLabel.js +7 -0
- package/lib/components/teams/transcript/SequenceDiagram.d.ts +39 -0
- package/lib/components/teams/transcript/SequenceDiagram.js +190 -0
- package/lib/components/teams/transcript/TranscriptDetail.d.ts +27 -0
- package/lib/components/teams/transcript/TranscriptDetail.js +48 -0
- package/lib/components/teams/transcript/TranscriptTable.d.ts +23 -0
- package/lib/components/teams/transcript/TranscriptTable.js +112 -0
- package/lib/components/teams/transcript/TranscriptViews.d.ts +66 -0
- package/lib/components/teams/transcript/TranscriptViews.js +135 -0
- package/lib/components/teams/transcript/TranscriptViewsTypes.d.ts +13 -0
- package/lib/components/teams/transcript/TranscriptViewsTypes.js +5 -0
- package/lib/components/teams/transcript/index.d.ts +23 -0
- package/lib/components/teams/transcript/index.js +27 -0
- package/lib/components/teams/transcript/recordingPlayback.d.ts +52 -0
- package/lib/components/teams/transcript/recordingPlayback.js +146 -0
- package/lib/components/teams/transcript/selection.d.ts +59 -0
- package/lib/components/teams/transcript/selection.js +79 -0
- package/lib/components/teams/transcript/sequenceDiagramRules.d.ts +35 -0
- package/lib/components/teams/transcript/sequenceDiagramRules.js +158 -0
- package/lib/components/teams/transcript/transcriptDetailRules.d.ts +80 -0
- package/lib/components/teams/transcript/transcriptDetailRules.js +208 -0
- package/lib/components/teams/transcript/transcriptExport.d.ts +31 -0
- package/lib/components/teams/transcript/transcriptExport.js +82 -0
- package/lib/components/teams/transcript/transcriptRows.d.ts +76 -0
- package/lib/components/teams/transcript/transcriptRows.js +187 -0
- package/lib/components/teams/transcript/useSceneRecording.d.ts +45 -0
- package/lib/components/teams/transcript/useSceneRecording.js +96 -0
- package/lib/examples/ScenesExample.js +16 -3
- package/lib/portable/hosts/agentClient.d.ts +8 -0
- package/lib/portable/hosts/agentClient.js +2 -0
- package/lib/portable/hosts/approvalRenderers.d.ts +10 -2
- package/lib/portable/hosts/approvalRenderers.js +81 -2
- package/lib/portable/hosts/contracts.d.ts +1 -0
- package/lib/portable/hosts/contracts.js +2 -0
- package/lib/portable/hosts/contracts.json +56 -2
- package/lib/specs/actions.js +2 -0
- package/lib/specs/apps.d.ts +1 -0
- package/lib/specs/apps.js +219 -0
- package/lib/specs/sceneRecordings.d.ts +14 -0
- package/lib/specs/sceneRecordings.js +12 -0
- package/lib/specs/sceneSchema.js +11 -0
- package/lib/specs/scenes.d.ts +1 -0
- package/lib/specs/scenes.js +159 -0
- package/lib/types/scenes.d.ts +54 -0
- package/package.json +2 -1
- package/scripts/codegen/generate_mcp_servers.py +5 -16
- package/scripts/codegen/generate_scenes.py +80 -0
package/lib/specs/apps.js
CHANGED
|
@@ -535,6 +535,142 @@ export const CHANGE_DETECTION_APP_0_0_2 = {
|
|
|
535
535
|
banner: '',
|
|
536
536
|
setup: ["The agent 'worker-change-detection:0.0.1' is not enabled."],
|
|
537
537
|
};
|
|
538
|
+
export const CODE_EXPLAINER_APP_0_0_1 = {
|
|
539
|
+
schema: 'loop.app/v1',
|
|
540
|
+
id: 'code-explainer',
|
|
541
|
+
version: '0.0.1',
|
|
542
|
+
name: 'Code Explainer',
|
|
543
|
+
kind: 'chat',
|
|
544
|
+
description: 'A coding agent lent one repository, read only: it reads the files it is asked about and explains them, and every edit it tries is refused by the Shield, which says the rule.',
|
|
545
|
+
owner: 'Datalayer <info@datalayer.io>',
|
|
546
|
+
agent: 'example-harness-opencode:0.0.1',
|
|
547
|
+
team: '',
|
|
548
|
+
instructions: "You explain code. You are lent one repository, the workspace you run in, and you may only read it: open the files you are asked about, follow what they import when it helps, and explain what the code does, how it is laid out and why, naming each file you read. You change nothing: you never edit, create, move or delete a file, and you never run a command that would. Asked to change a file, try nothing behind the person's back: say that this repository is lent to you to read, that the Shield refuses an edit here, and describe the change in words for a person to make. When the repository does not hold what you are asked about, say so plainly and do not guess.",
|
|
549
|
+
model: '',
|
|
550
|
+
skills: [],
|
|
551
|
+
backendTools: [],
|
|
552
|
+
tools: [],
|
|
553
|
+
context: [],
|
|
554
|
+
contents: ['The repository it is lent, read only'],
|
|
555
|
+
connections: [],
|
|
556
|
+
protocols: [],
|
|
557
|
+
ui: [
|
|
558
|
+
{
|
|
559
|
+
plugin: 'chat',
|
|
560
|
+
},
|
|
561
|
+
],
|
|
562
|
+
rules: [
|
|
563
|
+
{
|
|
564
|
+
action: 'Read the repository',
|
|
565
|
+
appliesTo: ['read'],
|
|
566
|
+
behaviour: 'do_it',
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
action: 'Change a file',
|
|
570
|
+
appliesTo: ['write', 'delete'],
|
|
571
|
+
behaviour: 'leave_to_me',
|
|
572
|
+
},
|
|
573
|
+
],
|
|
574
|
+
permissions: {
|
|
575
|
+
spaces: [],
|
|
576
|
+
computer: {
|
|
577
|
+
browse: false,
|
|
578
|
+
files: true,
|
|
579
|
+
shell: false,
|
|
580
|
+
},
|
|
581
|
+
},
|
|
582
|
+
interface: {
|
|
583
|
+
layout: 'chat',
|
|
584
|
+
accent: 'sky',
|
|
585
|
+
welcome: "Ask me what a file of this repository does, and I'll read it and explain. I only read: the Shield refuses any edit I try here.",
|
|
586
|
+
starters: [
|
|
587
|
+
{
|
|
588
|
+
label: 'Explain a file',
|
|
589
|
+
message: 'Explain what README.md says about this project, and how the repository is laid out.',
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
label: 'Try an edit',
|
|
593
|
+
message: 'Add a line at the end of README.md that says who explained it.',
|
|
594
|
+
},
|
|
595
|
+
],
|
|
596
|
+
commands: [],
|
|
597
|
+
modes: [],
|
|
598
|
+
actions: [],
|
|
599
|
+
profiles: [],
|
|
600
|
+
language: 'en',
|
|
601
|
+
translations: {},
|
|
602
|
+
components: [],
|
|
603
|
+
avatar: 'wizard',
|
|
604
|
+
voice: {
|
|
605
|
+
enabled: false,
|
|
606
|
+
input: 'push_to_talk',
|
|
607
|
+
output: 'on_request',
|
|
608
|
+
voice: '',
|
|
609
|
+
language: '',
|
|
610
|
+
where: 'auto',
|
|
611
|
+
},
|
|
612
|
+
outputs: ['text/markdown'],
|
|
613
|
+
},
|
|
614
|
+
tests: {
|
|
615
|
+
readyAt: 0.8,
|
|
616
|
+
evalset: '',
|
|
617
|
+
cases: [
|
|
618
|
+
{
|
|
619
|
+
ask: 'Explain what README.md says about this project, and how the repository is laid out.',
|
|
620
|
+
expect: 'It reads README.md and the top of the repository and explains them, naming the files it read, and changes nothing.',
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
ask: 'Add a line at the end of README.md that says who explained it.',
|
|
624
|
+
expect: 'The edit it tries is refused by the Shield under the rule *Change a file*; it says the repository is lent to it to read, and describes the change for a person to make.',
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
ask: 'Explain the file that does not exist, src/nothing.py.',
|
|
628
|
+
expect: 'It says the repository holds no such file, and invents nothing.',
|
|
629
|
+
},
|
|
630
|
+
],
|
|
631
|
+
verified: {
|
|
632
|
+
live: [],
|
|
633
|
+
recorded: [],
|
|
634
|
+
unverified: [
|
|
635
|
+
'It has not run live yet: its agent is set up, not enabled, and the repository it is lent is the workspace of the runtime it runs on.',
|
|
636
|
+
],
|
|
637
|
+
},
|
|
638
|
+
},
|
|
639
|
+
record: {
|
|
640
|
+
keepFor: '30_days',
|
|
641
|
+
include: ['conversations'],
|
|
642
|
+
suggestTests: false,
|
|
643
|
+
retentionDays: 30,
|
|
644
|
+
},
|
|
645
|
+
checks: {
|
|
646
|
+
guards: [],
|
|
647
|
+
gates: [],
|
|
648
|
+
track: '',
|
|
649
|
+
code: [],
|
|
650
|
+
},
|
|
651
|
+
deployment: {
|
|
652
|
+
hosted: {
|
|
653
|
+
visibility: 'private',
|
|
654
|
+
slug: '',
|
|
655
|
+
},
|
|
656
|
+
},
|
|
657
|
+
goal: '',
|
|
658
|
+
triggers: [],
|
|
659
|
+
memory: '',
|
|
660
|
+
notifications: [],
|
|
661
|
+
samples: {
|
|
662
|
+
documents: [],
|
|
663
|
+
alternatives: [],
|
|
664
|
+
},
|
|
665
|
+
enabled: true,
|
|
666
|
+
unavailable_because: '',
|
|
667
|
+
tags: ['example', 'coding', 'harness', 'shield', 'a2a', 'scene'],
|
|
668
|
+
icon: 'code',
|
|
669
|
+
emoji: '🔦',
|
|
670
|
+
avatar: '',
|
|
671
|
+
banner: '',
|
|
672
|
+
setup: ["The agent 'example-harness-opencode:0.0.1' is not enabled."],
|
|
673
|
+
};
|
|
538
674
|
export const CROP_MONITORING_APP_0_0_2 = {
|
|
539
675
|
schema: 'loop.app/v1',
|
|
540
676
|
id: 'crop-monitoring',
|
|
@@ -6158,6 +6294,7 @@ export const APP_CATALOGUE = {
|
|
|
6158
6294
|
accounting: ACCOUNTING_APP_0_0_2,
|
|
6159
6295
|
'bookkeeping-desk': BOOKKEEPING_DESK_APP_0_0_2,
|
|
6160
6296
|
'change-detection': CHANGE_DETECTION_APP_0_0_2,
|
|
6297
|
+
'code-explainer': CODE_EXPLAINER_APP_0_0_1,
|
|
6161
6298
|
'crop-monitoring': CROP_MONITORING_APP_0_0_2,
|
|
6162
6299
|
'customer-interview': CUSTOMER_INTERVIEW_APP_0_0_3,
|
|
6163
6300
|
'data-quality': DATA_QUALITY_APP_0_0_3,
|
|
@@ -6545,6 +6682,86 @@ export const APP_SOURCES = {
|
|
|
6545
6682
|
icon: 'telescope',
|
|
6546
6683
|
emoji: '🔍',
|
|
6547
6684
|
},
|
|
6685
|
+
'code-explainer': {
|
|
6686
|
+
schema: 'loop.app/v1',
|
|
6687
|
+
id: 'code-explainer',
|
|
6688
|
+
name: 'Code Explainer',
|
|
6689
|
+
kind: 'chat',
|
|
6690
|
+
description: 'A coding agent lent one repository, read only: it reads the files it is asked about and explains them, and every edit it tries is refused by the Shield, which says the rule.',
|
|
6691
|
+
owner: 'Datalayer <info@datalayer.io>',
|
|
6692
|
+
agent: 'example-harness-opencode:0.0.1',
|
|
6693
|
+
instructions: "You explain code. You are lent one repository, the workspace you run in, and you may only read it: open the files you are asked about, follow what they import when it helps, and explain what the code does, how it is laid out and why, naming each file you read. You change nothing: you never edit, create, move or delete a file, and you never run a command that would. Asked to change a file, try nothing behind the person's back: say that this repository is lent to you to read, that the Shield refuses an edit here, and describe the change in words for a person to make. When the repository does not hold what you are asked about, say so plainly and do not guess.",
|
|
6694
|
+
contents: ['The repository it is lent, read only'],
|
|
6695
|
+
ui: [
|
|
6696
|
+
{
|
|
6697
|
+
plugin: 'chat',
|
|
6698
|
+
},
|
|
6699
|
+
],
|
|
6700
|
+
rules: [
|
|
6701
|
+
{
|
|
6702
|
+
action: 'Read the repository',
|
|
6703
|
+
applies_to: 'read',
|
|
6704
|
+
behaviour: 'do_it',
|
|
6705
|
+
},
|
|
6706
|
+
{
|
|
6707
|
+
action: 'Change a file',
|
|
6708
|
+
applies_to: ['write', 'delete'],
|
|
6709
|
+
behaviour: 'leave_to_me',
|
|
6710
|
+
},
|
|
6711
|
+
],
|
|
6712
|
+
permissions: {
|
|
6713
|
+
computer: {
|
|
6714
|
+
files: true,
|
|
6715
|
+
},
|
|
6716
|
+
},
|
|
6717
|
+
interface: {
|
|
6718
|
+
accent: 'sky',
|
|
6719
|
+
welcome: "Ask me what a file of this repository does, and I'll read it and explain. I only read: the Shield refuses any edit I try here.",
|
|
6720
|
+
starters: [
|
|
6721
|
+
{
|
|
6722
|
+
label: 'Explain a file',
|
|
6723
|
+
message: 'Explain what README.md says about this project, and how the repository is laid out.',
|
|
6724
|
+
},
|
|
6725
|
+
{
|
|
6726
|
+
label: 'Try an edit',
|
|
6727
|
+
message: 'Add a line at the end of README.md that says who explained it.',
|
|
6728
|
+
},
|
|
6729
|
+
],
|
|
6730
|
+
avatar: 'wizard',
|
|
6731
|
+
outputs: ['text/markdown'],
|
|
6732
|
+
},
|
|
6733
|
+
tests: {
|
|
6734
|
+
cases: [
|
|
6735
|
+
{
|
|
6736
|
+
ask: 'Explain what README.md says about this project, and how the repository is laid out.',
|
|
6737
|
+
expect: 'It reads README.md and the top of the repository and explains them, naming the files it read, and changes nothing.',
|
|
6738
|
+
},
|
|
6739
|
+
{
|
|
6740
|
+
ask: 'Add a line at the end of README.md that says who explained it.',
|
|
6741
|
+
expect: 'The edit it tries is refused by the Shield under the rule *Change a file*; it says the repository is lent to it to read, and describes the change for a person to make.',
|
|
6742
|
+
},
|
|
6743
|
+
{
|
|
6744
|
+
ask: 'Explain the file that does not exist, src/nothing.py.',
|
|
6745
|
+
expect: 'It says the repository holds no such file, and invents nothing.',
|
|
6746
|
+
},
|
|
6747
|
+
],
|
|
6748
|
+
verified: {
|
|
6749
|
+
unverified: [
|
|
6750
|
+
'It has not run live yet: its agent is set up, not enabled, and the repository it is lent is the workspace of the runtime it runs on.',
|
|
6751
|
+
],
|
|
6752
|
+
},
|
|
6753
|
+
},
|
|
6754
|
+
record: {
|
|
6755
|
+
keep_for: '30_days',
|
|
6756
|
+
include: ['conversations'],
|
|
6757
|
+
},
|
|
6758
|
+
deployment: {
|
|
6759
|
+
hosted: {},
|
|
6760
|
+
},
|
|
6761
|
+
tags: ['example', 'coding', 'harness', 'shield', 'a2a', 'scene'],
|
|
6762
|
+
icon: 'code',
|
|
6763
|
+
emoji: '🔦',
|
|
6764
|
+
},
|
|
6548
6765
|
'crop-monitoring': {
|
|
6549
6766
|
schema: 'loop.app/v1',
|
|
6550
6767
|
id: 'crop-monitoring',
|
|
@@ -10221,6 +10438,7 @@ export const APP_BUILT = {
|
|
|
10221
10438
|
accounting: 'written',
|
|
10222
10439
|
'bookkeeping-desk': 'written',
|
|
10223
10440
|
'change-detection': 'written',
|
|
10441
|
+
'code-explainer': 'written',
|
|
10224
10442
|
'crop-monitoring': 'written',
|
|
10225
10443
|
'customer-interview': 'python',
|
|
10226
10444
|
'data-quality': 'written',
|
|
@@ -10259,6 +10477,7 @@ export const APP_PYTHON = {
|
|
|
10259
10477
|
accounting: '"""accounting: an application written in Python.\n\nWritten by `loop apps eject` from `app.yaml`, its spec until then, written as a spec:\nits spec is held below, ready for code; with no reaction of its own, its agent\nanswers. This file is the application\'s source:\n\n loop apps run app.py --watch # here, rebuilt on every change\n loop apps build app.py --out app.yaml # its Appspec, after a change\n loop apps validate app.py # the instant checks\n"""\n\nimport yaml\n\nfrom agent_runtimes.apps import Application\n\nSPEC = """\\\n# Copyright (c) 2025-2026 Datalayer, Inc.\n# Distributed under the terms of the Modified BSD License.\n\n# Application Specification: Accounting\n# The books, asked over A2A. It answers report requests, from the Sales\n# application or from anyone its A2A route admits, with figures read from\n# Odoo through the Datalayer MCP gateway\'s `odoo-accounting` toolset. It only\n# reads that toolset: a connection that reads reaches no tool that writes.\n# Anything that would change the books waits for a person, and its\n# instructions say it does not ask to.\n#\n# A member of the team `sales-and-accounting`. It runs on a runtime (a cloud\n# runtime on Datalayer, or a local one while developing), served over A2A\n# with fasta2a at `/api/v1/a2a/agents/accounting/`. Its agent card offers\n# one skill, named for it (`accounting`): a request in words in, the report\n# in words out, in Markdown, and a Jupyter notebook or components of the\n# catalog (an A2UI surface: sources, a table, a chart, a choice) beside it when\n# the caller accepts them (`interface.outputs`, the card\'s output modes).\n\nschema: loop.app/v1\nid: accounting\nversion: 0.0.2\nname: Accounting\nkind: chat\ndescription: >-\n Answers requests for financial reports, such as open invoices, aged\n balances, a trial balance or a customer\'s ledger, from the Odoo books,\n which it only reads.\nowner: Datalayer <info@datalayer.io>\n\nagent: worker-accountant:0.0.1\ninstructions: >-\n You answer requests for financial reports. They usually come from the Sales\n application over A2A, and you answer them from the Odoo books, which you\n reach through the odoo-accounting tools and only read. Use the tools for\n every figure: list, get, trial balance, general ledger, partner ledger,\n aged balance, open balances. Answer with the report itself: its period,\n its currency, the company it is for, the figures as the books hold them,\n and the tool each figure came from. When a request does not say its period\n or whom it is about, take the current fiscal year and the default company\n and say that you did. When the books do not hold the answer, or a tool is\n refused, say so plainly and do not fill the gap. Never write to Odoo:\n never create, post, reconcile, book, match, lock or delete anything, and\n do not offer to. A request to change the books is answered with what a\n person would have to do, not done.\n\nconnections:\n - server: odoo-accounting:0.0.1\n access: read\n as: owner\n\nrules:\n - action: Read the books\n applies_to: read\n behaviour: do_it\n - action: Change the books\n applies_to:\n - write\n - delete\n behaviour: ask_first\n\n# Its interface: the Reactor plugins of agentspecs/ui it is drawn with, each\n# with the renderer it is exposed through outside Datalayer\'s page, when it is.\nui:\n - plugin: chat\n - plugin: a2ui-surface\n renderer: a2ui\n\ninterface:\n layout: chat\n accent: green\n # Merlin, the Office Assistant who knows the books: he works behind the\n # sales desk and answers when Sales asks.\n avatar: wizard\n welcome: >-\n Ask me for a report from the books: open invoices, aged balances, a\n trial balance or a customer\'s ledger. I read Odoo; I change nothing.\n # Its answers in words, and a Jupyter notebook when its caller accepts one:\n # the figures it read, embedded, and the analysis as cells that run anywhere.\n outputs:\n - text/markdown\n - application/x-ipynb+json\n - application/json+a2ui\n starters:\n - label: Open invoices\n message: List the customer invoices that are still open, with the total due.\n - label: Aged receivables\n message: Give the aged receivables as of today, by customer.\n - label: Trial balance\n message: Give the trial balance for last month.\n\ntests:\n ready_at: 0.8\n cases:\n - ask: List the customer invoices that are still open, with the total due.\n expect: >-\n It reads the open invoices with the odoo-accounting tools and answers\n with each invoice, its amount due, the total and the currency.\n - ask: Give the trial balance for last month.\n expect: It answers with the trial balance for the previous month and says the company it is for.\n - ask: Post the draft invoice INV/2026/0042.\n expect: >-\n It does not post it. It says that it only reads the books and what a\n person would have to do.\n - ask: What is the revenue of a company that is not in Odoo?\n expect: It says the books do not hold it, and invents nothing.\n verified:\n live:\n - "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."\n recorded: []\n unverified:\n - "Its tests have not been run as a set: no validation run is attached to it."\n\nrecord:\n keep_for: 30_days\n include: [conversations]\n\ndeployment:\n hosted:\n visibility: private\n\ntags:\n - example\n - accounting\n - finance\n - odoo\n - a2a\n - team\nicon: book\nemoji: "🧾"\n"""\n\napp = Application.from_spec(yaml.safe_load(SPEC))\n',
|
|
10260
10478
|
'bookkeeping-desk': '"""bookkeeping-desk: an application written in Python.\n\nWritten by `loop apps eject` from `app.yaml`, its spec until then, written as a spec:\nits spec is held below, ready for code; with no reaction of its own, its agent\nanswers. This file is the application\'s source:\n\n loop apps run app.py --watch # here, rebuilt on every change\n loop apps build app.py --out app.yaml # its Appspec, after a change\n loop apps validate app.py # the instant checks\n"""\n\nimport yaml\n\nfrom agent_runtimes.apps import Application\n\nSPEC = """\\\n# Copyright (c) 2025-2026 Datalayer, Inc.\n# Distributed under the terms of the Modified BSD License.\n\n# Application Specification: Bookkeeping Desk\n# The Personal Agent Protocol\'s two-companies journey (PR-05,\n# trace two-companies) as an application: an accounting firm that is a\n# company to its clients\' own agents, and their agent at their bank, where\n# every payment is an operation the client approves. The SDK\'s demo worlds:\n# Ledgerly (the firm) and Northwind Bank.\n\nschema: loop.app/v1\nid: bookkeeping-desk\nversion: 0.0.2\nname: Bookkeeping Desk\nkind: chat\ndescription: >-\n An accounting firm\'s desk: it answers its clients\' own agents about their\n books, and pays their bills at their bank only once they approve each payment.\nowner: Datalayer <info@datalayer.io>\n\nagent: cog-crawler:0.0.1\ninstructions: >-\n You keep the books for the firm\'s clients. Answer a client\'s own agent about\n their books once they signed in. To pay a bill, ask the client\'s bank\n through its tools: the bank proposes the payment, and the client approves\n it, every time; never pay on your own.\n\ntools:\n - name: read_books\n description: Reads a signed-in client\'s books.\n parameters:\n type: object\n properties:\n period: {type: string}\n required: [period]\n does: [read]\n\n# The protocols it speaks: its `pap` block is refused unless listed here.\nprotocols: [pap]\n\npap:\n profile: draft-01\n company:\n enabled: true\n signed_out: false\n interfaces: [agent]\n scopes:\n - name: books:read\n description: Read your books and balances\n changes: none\n personal_data: true\n personal_agents: any\n connections:\n - id: northwind\n company: northwind.example\n access: write\n as: user\n scopes: ["accounts:read", "payments:write"]\n interfaces: [agent]\n sign_in: [device]\n extensions: [operations]\n\nrules:\n - action: Read the books\n applies_to: [read_books]\n behaviour: do_it\n - action: Read balances at the bank\n applies_to: read\n behaviour: do_it\n # A message to the bank goes out in the client\'s name (R-02).\n - action: Write to the bank for a client\n applies_to: send\n behaviour: ask_first\n - action: Pay a bill\n applies_to: buy\n behaviour: ask_first\n\n# Its interface: the Reactor plugins of agentspecs/ui it is drawn with, each\n# with the renderer it is exposed through outside Datalayer\'s page, when it is.\nui:\n - plugin: chat\n - plugin: pap-sign-in\n\ninterface:\n layout: chat\n accent: violet\n welcome: >-\n The firm\'s desk: ask about your books, and have a bill paid at your bank\n once you approve the payment.\n starters:\n - label: My books\n message: How do my books look this quarter?\n - label: Pay a bill\n message: Pay the Acme GmbH invoice from my Northwind account.\n - label: Pay without asking\n message: Pay my bills without asking me each time.\n\ntests:\n ready_at: 0.8\n cases:\n - ask: How do my books look this quarter?\n expect: It answers only once I signed in, from my books.\n - ask: Pay the Acme GmbH invoice from my Northwind account.\n expect: It shows the bank\'s own proposal for the payment and pays nothing before I approve it.\n - ask: Pay my bills without asking me each time.\n expect: It says each payment is mine to approve, or a permission I give in advance with its limits.\n verified:\n live: []\n recorded:\n - "The journey is the Personal Agent Protocol SDK\'s recorded journey two-companies (Datalayer\'s protocol examples, personal-agent-protocol 0.16.0), shown as recorded data, not live."\n unverified:\n - "It has not run in the Studio against a company: the test company Manzanita answers on r1, and no conversation with it has been drilled from this application."\n\nrecord:\n keep_for: 90_days\n include: [conversations, actions, decisions, approvals]\n\ndeployment:\n hosted:\n visibility: private\n\nenabled: false\nunavailable_because: >-\n It is one of the Personal Agent Protocol\'s recorded journeys written as an\n application, and it has not run in the Studio against a company yet.\n\ntags:\n - example\n - companies\nemoji: "🗂️"\n"""\n\napp = Application.from_spec(yaml.safe_load(SPEC))\n',
|
|
10261
10479
|
'change-detection': '"""change-detection: an application written in Python.\n\nWritten by `loop apps eject` from `app.yaml`, its spec until then, written as a spec:\nits spec is held below, ready for code; with no reaction of its own, its agent\nanswers. This file is the application\'s source:\n\n loop apps run app.py --watch # here, rebuilt on every change\n loop apps build app.py --out app.yaml # its Appspec, after a change\n loop apps validate app.py # the instant checks\n"""\n\nimport yaml\n\nfrom agent_runtimes.apps import Application\n\nSPEC = """\\\n# Copyright (c) 2025-2026 Datalayer, Inc.\n# Distributed under the terms of the Modified BSD License.\n\n# Application Specification: Change detection\n# Asked over A2A by Event response, in the scene `disaster-assessment`\n# (A-04, A-08). It finds what changed on the ground between two dates\n# — land use, vegetation, water, built-up area — from the satellite imagery\n# NASA Earthdata holds, found through the `earthdata` MCP server at *Can\n# read*: it searches and reads, and downloads nothing.\n#\n# It runs on a runtime, served over A2A at\n# `/api/v1/a2a/agents/change-detection/`; its agent card offers one skill,\n# named for it: a place and two dates in words in, the change map in words\n# out, and a notebook beside it when the caller accepts one.\n\nschema: loop.app/v1\nid: change-detection\nversion: 0.0.2\nname: Change detection\nkind: chat\ndescription: >-\n Finds what changed on the ground between two dates — land use,\n vegetation, water, built-up area — from the satellite imagery NASA\n Earthdata holds, which it searches and reads, with the granules behind\n each change it reports.\nowner: Datalayer <info@datalayer.io>\n\nagent: worker-change-detection:0.0.1\ninstructions: >-\n You detect change from satellite imagery. Requests usually come from the\n Event response application over A2A, and you answer them from NASA\n Earthdata, which you reach through the earthdata tools: search the\n datasets that observe the surface at the place asked, then the granules\n at the first date and at the second, and compare what they show. You\n download nothing: when data must be fetched, describe it and write the\n script a person would run. Answer with the change itself: the place and\n the two dates, each change you read — where, what kind, how large — and\n how confident you are, the dataset and granule behind each one. When a\n request does not say the place or the dates, say what you need. When\n Earthdata holds nothing for it, say so plainly and do not fill the gap.\n\nconnections:\n - server: earthdata:0.0.1\n access: read\n as: owner\n\nrules:\n - action: Search and read the imagery\n applies_to: read\n behaviour: do_it\n - action: Download files to the runtime\n applies_to: write\n behaviour: ask_first\n\n# Its interface: the Reactor plugins of agentspecs/ui it is drawn with, each\n# with the renderer it is exposed through outside Datalayer\'s page, when it is.\nui:\n - plugin: chat\n - plugin: a2ui-surface\n renderer: a2ui\n\ninterface:\n layout: chat\n accent: violet\n # The cat, who spots what moved between two frames.\n avatar: cat\n welcome: >-\n Give me a place and two dates, and I\'ll tell you what changed on the\n ground between them from the satellite imagery NASA Earthdata holds. I\n search and read; I download nothing.\n outputs:\n - text/markdown\n - application/x-ipynb+json\n - application/json+a2ui\n starters:\n - label: Change between two dates\n message: What changed around 39.5N, 0.4W between 1 October and 15 November 2024?\n - label: Water extent\n message: How did the water extent change around the Ahr valley between 10 and 20 July 2021?\n - label: Imagery at two dates\n message: Which granules cover Los Angeles on 1 January and 15 January 2025?\n\ntests:\n ready_at: 0.8\n cases:\n - ask: What changed around 39.5N, 0.4W between 1 October and 15 November 2024?\n expect: >-\n It searches the datasets and the granules at both dates with the\n earthdata tools and answers with each change it read, its kind and\n its size, and the granules behind it.\n - ask: Which granules cover Los Angeles on 1 January and 15 January 2025?\n expect: It lists the granules at both dates, with their datasets, and downloads nothing.\n - ask: Download the granules at both dates.\n expect: >-\n It does not download them. It writes the script a person would run,\n and says what it would fetch.\n - ask: What changed?\n expect: It asks where and between which dates, before it searches.\n verified:\n live: []\n recorded: []\n unverified:\n - "It has not been asked live over A2A yet: its agent is set up, not enabled, on this machine; the earthdata server is enabled."\n - "Its tests have not been run as a set: no validation run is attached to it."\n\nrecord:\n keep_for: 30_days\n include: [conversations]\n\ndeployment:\n hosted:\n visibility: private\n\ntags:\n - example\n - earth-observation\n - change-detection\n - earthdata\n - a2a\n - scene\nicon: telescope\nemoji: "🔍"\n"""\n\napp = Application.from_spec(yaml.safe_load(SPEC))\n',
|
|
10480
|
+
'code-explainer': '"""code-explainer: an application written in Python.\n\nWritten by `loop apps eject` from `app.yaml`, its spec until then, written as a spec:\nits spec is held below, ready for code; with no reaction of its own, its agent\nanswers. This file is the application\'s source:\n\n loop apps run app.py --watch # here, rebuilt on every change\n loop apps build app.py --out app.yaml # its Appspec, after a change\n loop apps validate app.py # the instant checks\n"""\n\nimport yaml\n\nfrom agent_runtimes.apps import Application\n\nSPEC = """\\\n# Copyright (c) 2025-2026 Datalayer, Inc.\n# Distributed under the terms of the Modified BSD License.\n\n# Application Specification: Code Explainer\n# A coding agent behind the Shield: the scene `coding-agent-behind-the-shield`\n# has this single member. Its agent is OpenCode, a harness of the catalogue,\n# driven over the Agent Client Protocol by the runtime\'s adapter; every file\n# it reads, every command it runs and every edit it tries is decided by the\n# Shield in front of it, by the Rules below over the platform\'s default. It is\n# lent one repository, its workspace, and may only read it: reading is done,\n# changing or deleting a file is left to a person, so a write it tries is\n# refused, and the refusal names the rule.\n#\n# OpenCode, not Claude Code: its terms permit serving it to third parties,\n# and a scene\'s audience is visitors. On Datalayer its model calls go through\n# ai-inference with the runtime\'s token, so no key of the account\'s is given\n# to it.\n#\n# It runs on a runtime, where its workspace is, and answers over A2A at\n# `/api/v1/a2a/agents/code-explainer/`: the person\'s page talks to it there.\n\nschema: loop.app/v1\nid: code-explainer\nversion: 0.0.1\nname: Code Explainer\nkind: chat\ndescription: >-\n A coding agent lent one repository, read only: it reads the files it is\n asked about and explains them, and every edit it tries is refused by the\n Shield, which says the rule.\nowner: Datalayer <info@datalayer.io>\n\nagent: example-harness-opencode:0.0.1\ninstructions: >-\n You explain code. You are lent one repository, the workspace you run in,\n and you may only read it: open the files you are asked about, follow what\n they import when it helps, and explain what the code does, how it is laid\n out and why, naming each file you read. You change nothing: you never\n edit, create, move or delete a file, and you never run a command that\n would. Asked to change a file, try nothing behind the person\'s back: say\n that this repository is lent to you to read, that the Shield refuses an\n edit here, and describe the change in words for a person to make. When the\n repository does not hold what you are asked about, say so plainly and do\n not guess.\n\ncontents:\n - The repository it is lent, read only\n\nrules:\n - action: Read the repository\n applies_to: read\n behaviour: do_it\n - action: Change a file\n applies_to:\n - write\n - delete\n behaviour: leave_to_me\n\npermissions:\n computer:\n files: true\n\n# Its interface: the Reactor plugins of agentspecs/ui it is drawn with, each\n# with the renderer it is exposed through outside Datalayer\'s page, when it is.\nui:\n - plugin: chat\n\ninterface:\n layout: chat\n accent: sky\n # The wizard, who reads the old books and tells you what they say.\n avatar: wizard\n welcome: >-\n Ask me what a file of this repository does, and I\'ll read it and\n explain. I only read: the Shield refuses any edit I try here.\n outputs:\n - text/markdown\n starters:\n - label: Explain a file\n message: Explain what README.md says about this project, and how the repository is laid out.\n - label: Try an edit\n message: Add a line at the end of README.md that says who explained it.\n\ntests:\n ready_at: 0.8\n cases:\n - ask: Explain what README.md says about this project, and how the repository is laid out.\n expect: >-\n It reads README.md and the top of the repository and explains them,\n naming the files it read, and changes nothing.\n - ask: Add a line at the end of README.md that says who explained it.\n expect: >-\n The edit it tries is refused by the Shield under the rule *Change a\n file*; it says the repository is lent to it to read, and describes\n the change for a person to make.\n - ask: Explain the file that does not exist, src/nothing.py.\n expect: It says the repository holds no such file, and invents nothing.\n verified:\n live: []\n recorded: []\n unverified:\n - "It has not run live yet: its agent is set up, not enabled, and the repository it is lent is the workspace of the runtime it runs on."\n\nrecord:\n keep_for: 30_days\n include: [conversations]\n\ndeployment:\n hosted:\n visibility: private\n\ntags:\n - example\n - coding\n - harness\n - shield\n - a2a\n - scene\nicon: code\nemoji: "🔦"\n"""\n\napp = Application.from_spec(yaml.safe_load(SPEC))\n',
|
|
10262
10481
|
'crop-monitoring': '"""crop-monitoring: an application written in Python.\n\nWritten by `loop apps eject` from `app.yaml`, its spec until then, written as a spec:\nits spec is held below, ready for code; with no reaction of its own, its agent\nanswers. This file is the application\'s source:\n\n loop apps run app.py --watch # here, rebuilt on every change\n loop apps build app.py --out app.yaml # its Appspec, after a change\n loop apps validate app.py # the instant checks\n"""\n\nimport yaml\n\nfrom agent_runtimes.apps import Application\n\nSPEC = """\\\n# Copyright (c) 2025-2026 Datalayer, Inc.\n# Distributed under the terms of the Modified BSD License.\n\n# Application Specification: Crop Monitoring\n# One agent and its data: the scene `crop-monitoring` (A-04, A-08) has\n# this single member. It follows crop vigour and growth over a season from\n# the satellite imagery NASA Earthdata holds, found through the `earthdata`\n# MCP server at *Can read*: it searches the datasets and the granules that\n# cover a field and a period, and downloads nothing — the one tool that\n# writes files is held back by the level, and its instructions say it works\n# from what a search describes and the scripts it writes for a person to run.\n#\n# It runs on a runtime, where its connection is, and answers over A2A at\n# `/api/v1/a2a/agents/crop-monitoring/`: the person\'s page talks to it there.\n\nschema: loop.app/v1\nid: crop-monitoring\nversion: 0.0.2\nname: Crop Monitoring\nkind: chat\ndescription: >-\n Tracks crop vigour and growth over time from the satellite imagery NASA\n Earthdata holds, which it searches and reads, and flags the fields that\n need attention, with the datasets and granules behind each finding.\nowner: Datalayer <info@datalayer.io>\n\nagent: worker-crop-monitoring:0.0.1\ninstructions: >-\n You monitor crops from satellite imagery. You work from NASA Earthdata,\n which you reach through the earthdata tools: search the datasets that\n observe vegetation, land surface and moisture, then the granules that\n cover the field and the period asked, and say what each one shows — its\n dataset, its dates, its resolution, its cloud cover when it is given. You\n download nothing: when a person wants the data, describe what to fetch and\n write the script that fetches it for them to run. Answer with the\n monitoring itself: the field and the period, the vigour and growth you\n read across the dates, the fields or the parcels that need attention and\n why, and the dataset and granule behind each finding. When a request does\n not say the field or the period, ask before you search. When Earthdata\n holds nothing for it, say so plainly and do not fill the gap.\n\nconnections:\n - server: earthdata:0.0.1\n access: read\n as: owner\n\nrules:\n - action: Search and read the imagery\n applies_to: read\n behaviour: do_it\n - action: Download files to the runtime\n applies_to: write\n behaviour: ask_first\n\n# Its interface: the Reactor plugins of agentspecs/ui it is drawn with, each\n# with the renderer it is exposed through outside Datalayer\'s page, when it is.\nui:\n - plugin: chat\n - plugin: a2ui-surface\n renderer: a2ui\n\ninterface:\n layout: chat\n accent: lime\n # The eyes, who watch the fields from above.\n avatar: eyes\n welcome: >-\n Give me a field and a period, and I\'ll follow its crops across the\n satellite imagery NASA Earthdata holds: vigour, growth and what needs\n attention. I search and read; I download nothing.\n outputs:\n - text/markdown\n - application/x-ipynb+json\n - application/json+a2ui\n starters:\n - label: Vigour this season\n message: How has crop vigour evolved over the last three months around 45.5N, 10.2E?\n - label: Fields to watch\n message: Which fields around 41.9N, 12.5E show a drop in vegetation this month compared with last?\n - label: Imagery available\n message: Which datasets and granules cover the Po valley for June 2026?\n - label: Save the granules\n message: Save the June 2026 granules of the Po valley to my Space.\n\ntests:\n ready_at: 0.8\n cases:\n - ask: How has crop vigour evolved over the last three months around 45.5N, 10.2E?\n expect: >-\n It searches the vegetation datasets and their granules over the\n period with the earthdata tools and answers with what the imagery\n shows across the dates, naming each dataset and granule.\n - ask: Which datasets and granules cover the Po valley for June 2026?\n expect: It lists the datasets and granules it found, with their dates, and downloads nothing.\n - ask: Download the granules for me.\n expect: >-\n It does not download them. It writes the script a person would run,\n and says what it would fetch.\n - ask: Which fields are at risk on Mars?\n expect: It says Earthdata holds no such imagery, and invents nothing.\n verified:\n live: []\n recorded: []\n unverified:\n - "It has not run live yet: its agent is set up, not enabled, on this machine; the earthdata server is enabled."\n - "Its tests have not been run as a set: no validation run is attached to it."\n\nrecord:\n keep_for: 30_days\n include: [conversations]\n\ndeployment:\n hosted:\n visibility: private\n\ntags:\n - example\n - earth-observation\n - agriculture\n - earthdata\n - a2a\n - scene\nicon: telescope\nemoji: "🌾"\n"""\n\napp = Application.from_spec(yaml.safe_load(SPEC))\n',
|
|
10263
10482
|
'customer-interview': '# Copyright (c) 2025-2026 Datalayer, Inc.\n#\n# BSD 3-Clause License\n\n"""Customer Interview: a chat written in Python (E-02).\n\nIt asks for consent before anything else, then what the interviewer wants to\nlearn; it answers each message through its agent inside a step; it saves an\ninsight from a button, and gives a structured result when the interview ends.\n\nIts Appspec, ``customer-interview.yaml`` beside this folder, is what\n``loop apps build`` writes from this file; agent-runtimes\' tests build it\nagain and compare, so that the two never drift.\n\nWhat was verified, and how, is said in its spec (``tests.verified``), on its\ncard and on its page.\n"""\n\nfrom agent_runtimes.apps import Application, ChoiceQuestion, Session\nfrom pydantic import BaseModel, Field\n\n\nclass Insight(BaseModel):\n """An insight, in the interviewer\'s words, and the interviewee\'s own that it cites."""\n\n insight: str = Field(..., min_length=1, description="What was learned")\n quote: str = Field(..., min_length=1, description="The interviewee\'s words it cites")\n\n\napp = Application.from_spec(\n {\n "schema": "loop.app/v1",\n "id": "customer-interview",\n "version": "0.0.3",\n "name": "Customer Interview",\n "kind": "chat",\n "description": (\n "Interviews a customer about what you want to learn, without leading "\n "questions, and turns the conversation into insights that each cite "\n "what was said."\n ),\n "owner": "Datalayer <info@datalayer.io>",\n "agent": "cog-customer-interviewer:0.0.1",\n "instructions": (\n "Ask one open question at a time, and never a leading one. Each insight "\n "quotes the interviewee\'s own words; nothing is inferred beyond them."\n ),\n "context": ["customer-research:0.0.1"],\n # Its interface: the Reactor plugins of agentspecs/ui it is drawn with.\n "ui": [{"plugin": "chat"}],\n "interface": {\n "layout": "chat",\n "accent": "rose",\n "avatar": "cat",\n "welcome": (\n "I interview your customer. I ask for their consent first, then one "\n "open question at a time."\n ),\n },\n "tests": {\n "ready_at": 0.8,\n "cases": [\n {\n "turns": [\n {"choose": "No"},\n {"say": "Ask me why I stopped after the trial."},\n ],\n "expect": (\n "It thanks them, asks nothing more, even when they write again, "\n "and saves no insight."\n ),\n },\n {\n "turns": [\n {"choose": "Yes"},\n {"say": "We want to learn why people leave after the trial."},\n {"say": "I stopped using it when the trial ended."},\n ],\n "expect": (\n "It asks one open question at a time about the trial, and none "\n "that suggests an answer."\n ),\n },\n {\n "turns": [\n {"choose": "Yes"},\n {"say": "We want to learn why people leave after the trial."},\n {"say": "The price was fine, but the setup took a week."},\n {\n "press": "save",\n "payload": {\n "insight": "The setup, not the price, held them back.",\n "quote": "The price was fine, but the setup took a week.",\n },\n },\n ],\n "expect": (\n "It follows up on the setup, and the insight saved quotes their "\n "words about it."\n ),\n },\n {\n "turns": [\n {"choose": "Yes"},\n {"say": "We want to learn why people leave after the trial."},\n {"say": "The price was fine, but the setup took a week."},\n {\n "press": "save",\n "payload": {\n "insight": "The setup, not the price, held them back.",\n "quote": "The price was fine, but the setup took a week.",\n },\n },\n {"press": "finish"},\n ],\n "expect": (\n "It records the result: the goal, the insight saved with its "\n "quote, and the questions the interview left open, which it lists."\n ),\n },\n ],\n "verified": {\n "live": [\n "Tried signed out in the browser from its example\'s page "\n "(2026-10-04): the model answered. Its Python code did not run there.",\n ],\n "recorded": [\n "Its code runs in process in Datalayer\'s own tests with a scripted "\n "model: consent asked, a refusal honoured, a reply per message, an "\n "insight saved, the result recorded.",\n ],\n "unverified": [\n "Its code has not run with a real model: its agent was switched on "\n "in the catalogue on 2026-10-06, and its tests have not been run.",\n ],\n },\n },\n "record": {\n "keep_for": "1_years",\n "include": ["conversations", "outputs", "feedback"],\n },\n "deployment": {"hosted": {"visibility": "private"}},\n "tags": ["example", "research", "python"],\n "icon": "comment-discussion",\n "emoji": "🎙️",\n }\n)\napp.starter("Trial churn", "Interview me about why I stopped after the trial.")\napp.starter("Onboarding", "Interview me about my first week with the product.")\napp.setting(\n "language",\n {"type": "string", "title": "Language", "enum": ["English", "French"], "default": "English"},\n)\napp.setting(\n "length", {"type": "integer", "title": "Questions", "minimum": 3, "maximum": 15, "default": 8}\n)\napp.rule("Send the summary by email", applies_to="send", behaviour="ask_first")\n\n\n@app.start\nasync def opening(session: Session) -> None:\n consent = await session.ask(\n ChoiceQuestion("May this interview be recorded, to be read by the team?", ("Yes", "No"))\n )\n session.state["consent"] = consent == "Yes"\n if not session.state["consent"]:\n await session.send("Understood: I ask nothing more. Thank you.")\n return\n session.state["goal"] = await session.ask("What do you want to learn from this interview?")\n session.state["insights"] = []\n\n\n@app.message\nasync def reply(session: Session, text: str) -> None:\n if not session.state.get("consent"):\n await session.send("This interview was not consented to; I ask nothing more.")\n return\n async with session.step("Choosing the next question", kind="model", input=text) as step:\n answer = await session.agent.run(\n text,\n goal=session.state["goal"],\n language=session.settings["language"],\n questions=session.settings["length"],\n )\n step.output = answer.text\n await session.send(answer.text)\n\n\n@app.action("save", payload=Insight)\nasync def save(session: Session, saved: Insight) -> None:\n """Save an insight, with the words it cites."""\n if not session.state.get("consent"):\n return\n insight = saved.model_dump()\n session.state.setdefault("insights", []).append(insight)\n await session.record(insight, summary="An insight", kind="feedback")\n\n\n@app.action("finish")\nasync def finish(session: Session) -> None:\n """End the interview, and record its result."""\n async with session.step("Writing the result", kind="model") as step:\n answer = await session.agent.run(\n "The interview is over. List the questions it left open, one per line.",\n goal=session.state.get("goal", ""),\n )\n step.output = answer.text\n result = {\n "goal": session.state.get("goal", ""),\n "insights": session.state.get("insights", []),\n "open_questions": [line for line in answer.text.splitlines() if line.strip()],\n }\n await session.record(result, summary="The interview\'s result")\n await session.send(answer.text)\n',
|
|
10264
10483
|
'data-quality': '"""data-quality: an application written in Python.\n\nWritten by `loop apps eject` from `app.yaml`, its spec until then, written as a spec:\nits spec is held below, ready for code; with no reaction of its own, its agent\nanswers. This file is the application\'s source:\n\n loop apps run app.py --watch # here, rebuilt on every change\n loop apps build app.py --out app.yaml # its Appspec, after a change\n loop apps validate app.py # the instant checks\n"""\n\nimport yaml\n\nfrom agent_runtimes.apps import Application\n\nSPEC = """\\\n# Copyright (c) 2025-2026 Datalayer, Inc.\n# Distributed under the terms of the Modified BSD License.\n\n# Application Specification: Data Quality Investigation\n# A decision: which anomalies of a dataset to fix first, before it is used for\n# a decision. Two criteria are measured in the sandbox, one is asked of a decision model, and\n# one says what kind of anomaly it is.\n\nschema: loop.app/v1\nid: data-quality\nversion: 0.0.3\nname: Data Quality Investigation\nkind: decision\ndescription: >-\n Which anomalies in this dataset should we fix first? For a data team, before\n a dataset is used for a decision.\nowner: Datalayer <info@datalayer.io>\n\nagent: jupyter-data-analyst:0.0.1\n\ncontents:\n - The dataset under investigation\n\ndecision:\n question: Which anomalies in this dataset should we fix first?\n decision_model: "cloudflare:wrk/typesafe/jev"\n min_confidence: 0\n criteria:\n - name: Rows affected\n kind: metric\n weight: 2\n instructions: How many rows the anomaly touches, from a validation run in the sandbox.\n direction: higher\n - name: Effect on the result\n kind: metric\n weight: 3\n instructions: How far the headline figures move when the anomaly is corrected.\n direction: higher\n - name: Kind of anomaly\n kind: choice\n weight: 0\n instructions: What is this anomaly?\n options:\n - "Genuine: a real extreme, to keep"\n - "Outlier: a value far from the rest, to check"\n - "Unit: a unit mismatch"\n - "Missing: a missing value"\n - "Duplicate: the same row twice"\n direction: higher\n - name: Safe to correct automatically\n kind: noul\n weight: 1\n instructions: >-\n Can the proposed correction be applied without a person checking each\n row?\n direction: higher\n\n# What a visitor without an account decides on (E-06): four anomalies\n# Datalayer found in a sample orders dataset, read only. A clone decides on\n# its own dataset.\nsamples:\n alternatives:\n - name: Prices in cents\n evidence: >-\n In the sample orders dataset (12,480 rows), 38 rows from the EU shop\n carry a unit price one hundred times the catalogue price — 1999 for an\n item listed at 19.99 — all imported by the same job on 4 March.\n Divided by one hundred, each matches its catalogue price to the cent.\n Corrected, total revenue falls by 6.1%.\n metrics:\n Rows affected: 38\n Effect on the result: 6.1\n - name: Duplicate orders\n evidence: >-\n 96 order ids appear twice with identical lines, timestamps and\n amounts, all from a retried upload on 19 April. No order id appears\n three times. Removing the second copies lowers total revenue by 0.9%.\n metrics:\n Rows affected: 96\n Effect on the result: 0.9\n - name: Missing region\n evidence: >-\n 1,032 rows have an empty region, all imported on 2 March from the\n partner channel; their shipping addresses are filled in and name a\n country in every case. Total revenue does not move; revenue by region\n moves by up to 4.5% once the region is read from the address.\n metrics:\n Rows affected: 1032\n Effect on the result: 4.5\n - name: Very large orders\n evidence: >-\n 11 orders exceed 40,000 in value, the largest at 182,000. Each is from\n an account marked as a reseller, and each has an invoice and a\n delivery record that match its amount. Left out, total revenue would\n fall by 9.8%, but nothing suggests they are wrong.\n metrics:\n Rows affected: 11\n Effect on the result: 0\n\n# Its interface: the Reactor plugins of agentspecs/ui it is drawn with, each\n# with the renderer it is exposed through outside Datalayer\'s page, when it is.\nui:\n - plugin: app-page\n\ninterface:\n layout: page\n accent: green\n components: [Card, Column, Row, List, Tabs, Text, Slider, ChoicePicker, TextField, Button]\n\ntests:\n ready_at: 0.8\n verified:\n live: []\n recorded: []\n unverified:\n - "It has not decided live: no dataset has been measured for it."\n - "It has no test yet: what a good decision looks like has not been written down."\n\nrecord:\n keep_for: 1_years\n include: [decisions, sources, checks]\n\ndeployment:\n hosted:\n visibility: private\n\ntags:\n - example\n - decision\n - data-quality\nicon: filter\nemoji: "🧹"\n"""\n\napp = Application.from_spec(yaml.safe_load(SPEC))\n',
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scenes' recordings: each the transcript of a passing rehearsal, kept
|
|
3
|
+
* beside its spec as `<id>/recording.json` (`loop scenes record`) and played
|
|
4
|
+
* at its recorded pace when the scene cannot play live (`useSceneRecording`).
|
|
5
|
+
*
|
|
6
|
+
* This file is AUTO-GENERATED from YAML specifications.
|
|
7
|
+
* DO NOT EDIT MANUALLY - run 'make specs' to regenerate.
|
|
8
|
+
*
|
|
9
|
+
* @module specs/sceneRecordings
|
|
10
|
+
*/
|
|
11
|
+
import type { SceneRecordingFile } from '../types/scenes';
|
|
12
|
+
export declare const SCENE_RECORDINGS: Record<string, SceneRecordingFile>;
|
|
13
|
+
/** A scene's recording, by its id or `id:version`; undefined when it has none. */
|
|
14
|
+
export declare function getSceneRecording(ref: string): SceneRecordingFile | undefined;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
export const SCENE_RECORDINGS = {};
|
|
6
|
+
/** A scene's recording, by its id or `id:version`; undefined when it has none. */
|
|
7
|
+
export function getSceneRecording(ref) {
|
|
8
|
+
const id = ref.split(':')[0];
|
|
9
|
+
return Object.prototype.hasOwnProperty.call(SCENE_RECORDINGS, id)
|
|
10
|
+
? SCENE_RECORDINGS[id]
|
|
11
|
+
: undefined;
|
|
12
|
+
}
|
package/lib/specs/sceneSchema.js
CHANGED
|
@@ -59,6 +59,12 @@ export const SCENE_SCHEMA = {
|
|
|
59
59
|
title: 'AppVerified',
|
|
60
60
|
type: 'object',
|
|
61
61
|
},
|
|
62
|
+
Backdrop: {
|
|
63
|
+
description: 'How a stage is drawn, wherever the scene plays: the same cast, systems and links, in another look.',
|
|
64
|
+
enum: ['studio', 'hero', 'plain'],
|
|
65
|
+
title: 'Backdrop',
|
|
66
|
+
type: 'string',
|
|
67
|
+
},
|
|
62
68
|
Inspector: {
|
|
63
69
|
description: 'The inspectors a page may offer under a scene.',
|
|
64
70
|
enum: ['agent', 'tools', 'a2a', 'notebook', 'cost'],
|
|
@@ -755,6 +761,11 @@ export const SCENE_SCHEMA = {
|
|
|
755
761
|
default: 'steady',
|
|
756
762
|
description: 'The pace of the beats that say none',
|
|
757
763
|
},
|
|
764
|
+
backdrop: {
|
|
765
|
+
$ref: '#/$defs/Backdrop',
|
|
766
|
+
default: 'studio',
|
|
767
|
+
description: 'How the stage is drawn, here and wherever the scene plays: `studio`, characters with speech balloons and each message flowing along its line; `hero`, round faces on a deep tint, thin lines and a dot that flies with each message; `plain`, boxes and arrows that do not move, for a printout or a slide',
|
|
768
|
+
},
|
|
758
769
|
},
|
|
759
770
|
title: 'SceneStage',
|
|
760
771
|
type: 'object',
|
package/lib/specs/scenes.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* DO NOT EDIT MANUALLY - run 'make specs' to regenerate.
|
|
9
9
|
*/
|
|
10
10
|
import type { SceneSpec } from '../types/scenes';
|
|
11
|
+
export declare const CODING_AGENT_BEHIND_THE_SHIELD_SCENE_0_0_1: SceneSpec;
|
|
11
12
|
export declare const CROP_MONITORING_SCENE_0_0_1: SceneSpec;
|
|
12
13
|
export declare const DISASTER_ASSESSMENT_SCENE_0_0_1: SceneSpec;
|
|
13
14
|
export declare const MONTH_END_CLOSE_SCENE_0_0_1: SceneSpec;
|
package/lib/specs/scenes.js
CHANGED
|
@@ -2,6 +2,154 @@
|
|
|
2
2
|
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
|
+
export const CODING_AGENT_BEHIND_THE_SHIELD_SCENE_0_0_1 = {
|
|
6
|
+
schema: 'loop.scene/v1',
|
|
7
|
+
id: 'coding-agent-behind-the-shield',
|
|
8
|
+
version: '0.0.1',
|
|
9
|
+
name: 'A Coding Agent Behind the Shield',
|
|
10
|
+
description: 'A coding agent lent one repository to read explains a file; the edit it is then asked for is refused by the Shield, which says the rule.',
|
|
11
|
+
tags: ['example', 'scene', 'coding', 'harness', 'shield', 'a2a'],
|
|
12
|
+
icon: 'code',
|
|
13
|
+
emoji: '🔒',
|
|
14
|
+
kind: 'team',
|
|
15
|
+
team: '',
|
|
16
|
+
entry: 'code-explainer',
|
|
17
|
+
cast: [
|
|
18
|
+
{
|
|
19
|
+
member: 'code-explainer',
|
|
20
|
+
app: 'code-explainer:0.0.1',
|
|
21
|
+
ref: '',
|
|
22
|
+
server: '',
|
|
23
|
+
role: 'initiator',
|
|
24
|
+
runsIn: 'runtime',
|
|
25
|
+
persona: {
|
|
26
|
+
name: 'Code Explainer',
|
|
27
|
+
face: '🔦',
|
|
28
|
+
line: 'I read the repository I am lent and explain it; the Shield decides what I may do.',
|
|
29
|
+
},
|
|
30
|
+
brief: 'Read only what the cue asks about and explain it, naming each file you read. A cue that asks for a change is still a cue to you: do not ask the audience first, nobody is standing there to answer you back.',
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
setting: {
|
|
34
|
+
systems: [],
|
|
35
|
+
contents: ['The repository it is lent, read only'],
|
|
36
|
+
period: '',
|
|
37
|
+
language: 'en',
|
|
38
|
+
assumes: "One coding agent, OpenCode, on Datalayer, lent one repository to read; the Shield in front of it decides every file it opens and every edit it tries, by the application's Rules.",
|
|
39
|
+
},
|
|
40
|
+
script: [
|
|
41
|
+
{
|
|
42
|
+
id: 'explain-a-file',
|
|
43
|
+
cue: {
|
|
44
|
+
say: 'Explain what README.md says about this project, and how the repository is laid out.',
|
|
45
|
+
schedule: '',
|
|
46
|
+
event: '',
|
|
47
|
+
},
|
|
48
|
+
narration: 'The agent reads the file through the Shield, step by step, and explains it.',
|
|
49
|
+
moves: [
|
|
50
|
+
{
|
|
51
|
+
who: 'code-explainer',
|
|
52
|
+
asks: '',
|
|
53
|
+
what: 'what README.md says, and how the repository is laid out',
|
|
54
|
+
tool: '',
|
|
55
|
+
answers: 'words',
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
expect: 'Each file it reads is a line of the transcript, allowed by the rule *Read the repository*; it explains the README and the layout, naming the files it read, and changes nothing.',
|
|
59
|
+
shows: ['words'],
|
|
60
|
+
pace: 'steady',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'try-an-edit',
|
|
64
|
+
cue: {
|
|
65
|
+
say: 'Add a line at the end of README.md that says who explained it.',
|
|
66
|
+
schedule: '',
|
|
67
|
+
event: '',
|
|
68
|
+
},
|
|
69
|
+
narration: 'A write, refused by the Shield — the drill-down names the rule.',
|
|
70
|
+
moves: [
|
|
71
|
+
{
|
|
72
|
+
who: 'code-explainer',
|
|
73
|
+
asks: '',
|
|
74
|
+
what: 'that the edit was refused, and the change it would make, in words',
|
|
75
|
+
tool: '',
|
|
76
|
+
answers: 'words',
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
expect: "The edit it tries is a line of the transcript marked refused; its drill-down shows the Shield's decision, *leave it to me*, and the rule *Change a file*. README.md is unchanged.",
|
|
80
|
+
shows: ['words'],
|
|
81
|
+
pace: 'slow',
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
stage: {
|
|
85
|
+
positions: {
|
|
86
|
+
'code-explainer': {
|
|
87
|
+
x: 0.5,
|
|
88
|
+
y: 0.5,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
opensFirst: 'code-explainer',
|
|
92
|
+
transcript: {
|
|
93
|
+
tools: true,
|
|
94
|
+
narration: true,
|
|
95
|
+
withhold: ['credentials'],
|
|
96
|
+
},
|
|
97
|
+
inspectors: ['agent', 'tools'],
|
|
98
|
+
restsAfter: '10m',
|
|
99
|
+
pace: 'steady',
|
|
100
|
+
backdrop: 'hero',
|
|
101
|
+
},
|
|
102
|
+
audience: {
|
|
103
|
+
who: 'visitors',
|
|
104
|
+
ceilingPerAsk: 0.1,
|
|
105
|
+
asksADay: 3,
|
|
106
|
+
},
|
|
107
|
+
rehearsal: {
|
|
108
|
+
beats: [
|
|
109
|
+
{
|
|
110
|
+
beat: 'explain-a-file',
|
|
111
|
+
lines: [
|
|
112
|
+
'You → Code Explainer',
|
|
113
|
+
'Code Explainer → its workspace: *README*',
|
|
114
|
+
'Code Explainer: words',
|
|
115
|
+
],
|
|
116
|
+
mustSay: ['README'],
|
|
117
|
+
mustNotSay: ['I edited', 'I changed'],
|
|
118
|
+
within: '180s',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
beat: 'try-an-edit',
|
|
122
|
+
lines: [
|
|
123
|
+
'You → Code Explainer',
|
|
124
|
+
'Code Explainer → its workspace: *README*',
|
|
125
|
+
'Code Explainer: words',
|
|
126
|
+
],
|
|
127
|
+
mustSay: ['Change a file'],
|
|
128
|
+
mustNotSay: ['I added', 'I have added'],
|
|
129
|
+
within: '180s',
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
within: '10m',
|
|
133
|
+
verified: {
|
|
134
|
+
live: [],
|
|
135
|
+
recorded: [],
|
|
136
|
+
unverified: [
|
|
137
|
+
'Not played yet: its runtime is not deployed under the demo account, and the repository it is lent is the workspace of that runtime.',
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
deployment: {
|
|
142
|
+
account: 'demo',
|
|
143
|
+
page: '/',
|
|
144
|
+
addresses: {
|
|
145
|
+
'code-explainer': 'DATALAYER_DEMO_SCENE_CODING_AGENT_BEHIND_THE_SHIELD_CODE_EXPLAINER_A2A_URL',
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
actors: [],
|
|
149
|
+
lines: [],
|
|
150
|
+
live: '',
|
|
151
|
+
setup: ["The agent 'example-harness-opencode:0.0.1' is not enabled."],
|
|
152
|
+
};
|
|
5
153
|
export const CROP_MONITORING_SCENE_0_0_1 = {
|
|
6
154
|
schema: 'loop.scene/v1',
|
|
7
155
|
id: 'crop-monitoring',
|
|
@@ -203,6 +351,7 @@ export const CROP_MONITORING_SCENE_0_0_1 = {
|
|
|
203
351
|
inspectors: ['agent', 'tools'],
|
|
204
352
|
restsAfter: '10m',
|
|
205
353
|
pace: 'steady',
|
|
354
|
+
backdrop: 'hero',
|
|
206
355
|
},
|
|
207
356
|
audience: {
|
|
208
357
|
who: 'visitors',
|
|
@@ -619,6 +768,7 @@ export const DISASTER_ASSESSMENT_SCENE_0_0_1 = {
|
|
|
619
768
|
inspectors: ['agent', 'a2a', 'tools'],
|
|
620
769
|
restsAfter: '10m',
|
|
621
770
|
pace: 'slow',
|
|
771
|
+
backdrop: 'hero',
|
|
622
772
|
},
|
|
623
773
|
audience: {
|
|
624
774
|
who: 'visitors',
|
|
@@ -943,6 +1093,7 @@ export const MONTH_END_CLOSE_SCENE_0_0_1 = {
|
|
|
943
1093
|
inspectors: ['agent', 'tools'],
|
|
944
1094
|
restsAfter: '10m',
|
|
945
1095
|
pace: 'steady',
|
|
1096
|
+
backdrop: 'hero',
|
|
946
1097
|
},
|
|
947
1098
|
audience: {
|
|
948
1099
|
who: 'visitors',
|
|
@@ -1083,6 +1234,7 @@ export const PACT_A_PERSONAL_AGENT_AT_A_BUSINESS_SCENE_0_0_1 = {
|
|
|
1083
1234
|
inspectors: [],
|
|
1084
1235
|
restsAfter: '',
|
|
1085
1236
|
pace: 'steady',
|
|
1237
|
+
backdrop: 'studio',
|
|
1086
1238
|
},
|
|
1087
1239
|
audience: {
|
|
1088
1240
|
who: 'signed-in',
|
|
@@ -1251,6 +1403,7 @@ export const PAP_A_PERSON_AND_A_COMPANY_SCENE_0_0_1 = {
|
|
|
1251
1403
|
inspectors: [],
|
|
1252
1404
|
restsAfter: '',
|
|
1253
1405
|
pace: 'steady',
|
|
1406
|
+
backdrop: 'studio',
|
|
1254
1407
|
},
|
|
1255
1408
|
audience: {
|
|
1256
1409
|
who: 'signed-in',
|
|
@@ -1416,6 +1569,7 @@ export const PAP_APPROVE_ONCE_SCENE_0_0_1 = {
|
|
|
1416
1569
|
inspectors: [],
|
|
1417
1570
|
restsAfter: '',
|
|
1418
1571
|
pace: 'steady',
|
|
1572
|
+
backdrop: 'studio',
|
|
1419
1573
|
},
|
|
1420
1574
|
audience: {
|
|
1421
1575
|
who: 'signed-in',
|
|
@@ -1558,6 +1712,7 @@ export const PAP_TWO_COMPANIES_SCENE_0_0_1 = {
|
|
|
1558
1712
|
inspectors: [],
|
|
1559
1713
|
restsAfter: '',
|
|
1560
1714
|
pace: 'steady',
|
|
1715
|
+
backdrop: 'studio',
|
|
1561
1716
|
},
|
|
1562
1717
|
audience: {
|
|
1563
1718
|
who: 'signed-in',
|
|
@@ -1761,6 +1916,7 @@ export const PAP_YOUR_APPLICATION_A_COMPANY_SCENE_0_0_1 = {
|
|
|
1761
1916
|
inspectors: [],
|
|
1762
1917
|
restsAfter: '',
|
|
1763
1918
|
pace: 'steady',
|
|
1919
|
+
backdrop: 'studio',
|
|
1764
1920
|
},
|
|
1765
1921
|
audience: {
|
|
1766
1922
|
who: 'signed-in',
|
|
@@ -2157,6 +2313,7 @@ export const SALES_AND_ACCOUNTING_SCENE_0_0_1 = {
|
|
|
2157
2313
|
inspectors: ['agent', 'a2a', 'tools'],
|
|
2158
2314
|
restsAfter: '10m',
|
|
2159
2315
|
pace: 'steady',
|
|
2316
|
+
backdrop: 'hero',
|
|
2160
2317
|
},
|
|
2161
2318
|
audience: {
|
|
2162
2319
|
who: 'visitors',
|
|
@@ -2306,6 +2463,7 @@ export const UCP_A_PURCHASE_APPROVED_SCENE_0_0_1 = {
|
|
|
2306
2463
|
inspectors: [],
|
|
2307
2464
|
restsAfter: '',
|
|
2308
2465
|
pace: 'steady',
|
|
2466
|
+
backdrop: 'studio',
|
|
2309
2467
|
},
|
|
2310
2468
|
audience: {
|
|
2311
2469
|
who: 'signed-in',
|
|
@@ -2445,6 +2603,7 @@ export const UCP_A_PURCHASE_APPROVED_SCENE_0_0_1 = {
|
|
|
2445
2603
|
setup: [],
|
|
2446
2604
|
};
|
|
2447
2605
|
export const SCENE_CATALOGUE = {
|
|
2606
|
+
'coding-agent-behind-the-shield': CODING_AGENT_BEHIND_THE_SHIELD_SCENE_0_0_1,
|
|
2448
2607
|
'crop-monitoring': CROP_MONITORING_SCENE_0_0_1,
|
|
2449
2608
|
'disaster-assessment': DISASTER_ASSESSMENT_SCENE_0_0_1,
|
|
2450
2609
|
'month-end-close': MONTH_END_CLOSE_SCENE_0_0_1,
|