@datalayer/agent-runtimes 1.3.95 → 1.3.97

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 (37) hide show
  1. package/dist-embed/datalayer-app-main.js +6 -6
  2. package/dist-embed/datalayer-app.js +1 -1
  3. package/lib/apps/__tests__/fixtures/sceneCheckCases.json +352 -0
  4. package/lib/apps/apps/sceneChecks.d.ts +92 -0
  5. package/lib/apps/apps/sceneChecks.js +649 -0
  6. package/lib/apps/apps/sceneShape.d.ts +8 -0
  7. package/lib/apps/apps/sceneShape.js +191 -0
  8. package/lib/apps/apps/sceneYaml.d.ts +64 -0
  9. package/lib/apps/apps/sceneYaml.js +253 -0
  10. package/lib/apps/core/canvasParts.d.ts +28 -0
  11. package/lib/apps/core/canvasParts.js +14 -0
  12. package/lib/apps/core/index.d.ts +1 -0
  13. package/lib/apps/core/index.js +2 -0
  14. package/lib/apps/embed/loader.js +10 -0
  15. package/lib/apps/plugins/agents/SandboxSelector.js +13 -2
  16. package/lib/apps/plugins/agents/host.d.ts +2 -0
  17. package/lib/apps/plugins/agents/host.js +16 -0
  18. package/lib/apps/plugins/agents/plugin.js +10 -2
  19. package/lib/apps/plugins/agents/switchable.d.ts +12 -0
  20. package/lib/apps/plugins/agents/switchable.js +28 -0
  21. package/lib/apps/plugins/canvas-parts/index.d.ts +44 -0
  22. package/lib/apps/plugins/canvas-parts/index.js +209 -0
  23. package/lib/chat/base/loopStep.d.ts +2 -1
  24. package/lib/chat/base/loopStep.js +2 -3
  25. package/lib/chat/tools/ToolCallDisplay.js +2 -1
  26. package/lib/hooks/useAgUi.js +4 -1
  27. package/lib/protocols/AGUIAdapter.js +5 -1
  28. package/lib/protocols/agUiRefusal.d.ts +47 -0
  29. package/lib/protocols/agUiRefusal.js +87 -0
  30. package/lib/specs/agents/agents.d.ts +1 -0
  31. package/lib/specs/agents/agents.js +49 -0
  32. package/lib/specs/apps.js +20 -12
  33. package/lib/specs/scenes.js +103 -98
  34. package/lib/specs/teams/teams.js +1 -1
  35. package/package.json +4 -2
  36. package/scripts/dist-embed.mjs +121 -0
  37. package/scripts/record-scene-checks.py +600 -0
@@ -1 +1 @@
1
- (()=>{(function(){const c="datalayer-app-main.js",t=document.currentScript;if(!t||!t.src){console.error("datalayer-app: the script has to be loaded by a script tag with a src, so it knows where its module is.");return}const e=new URL(c,t.src).href;import(e).catch(a=>{const s=a instanceof Error?a.message:String(a),o=`The application could not be loaded: ${e} did not load (${s}).`;console.error(`datalayer-app: ${o}`),document.querySelectorAll("datalayer-app").forEach(n=>{if(n.shadowRoot)return;const r=document.createElement("p");r.textContent=o,n.attachShadow({mode:"open"}).appendChild(r)})})})();})();
1
+ (()=>{(function(){const s="datalayer-app-main.js",t=document.currentScript;if(!t||!t.src){console.error("datalayer-app: the script has to be loaded by a script tag with a src, so it knows where its module is.");return}const n=window;n.__webpack_public_path__===void 0&&(n.__webpack_public_path__="");const e=new URL(s,t.src).href;import(e).catch(a=>{const i=a instanceof Error?a.message:String(a),o=`The application could not be loaded: ${e} did not load (${i}).`;console.error(`datalayer-app: ${o}`),document.querySelectorAll("datalayer-app").forEach(c=>{if(c.shadowRoot)return;const r=document.createElement("p");r.textContent=o,c.attachShadow({mode:"open"}).appendChild(r)})})})();})();
@@ -0,0 +1,352 @@
1
+ {
2
+ "says": "What `loop` refuses of a scene, recorded from agentspecs (plans/STUDIO.md S-10).",
3
+ "recordedWith": "scripts/record-scene-checks.py",
4
+ "cases": [
5
+ {
6
+ "name": "a scene with nobody on stage",
7
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\n",
8
+ "says": [
9
+ "probe: the scene stages nobody: it names a `team`, or writes its `cast`"
10
+ ]
11
+ },
12
+ {
13
+ "name": "the entry is not in the cast",
14
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: nobody\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n",
15
+ "says": ["The scene enters at 'nobody', which is not in its cast."]
16
+ },
17
+ {
18
+ "name": "the entry is a system",
19
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: odoo\ncast:\n - member: odoo\n server: odoo-accounting\n persona:\n name: Odoo\n",
20
+ "says": [
21
+ "Its cast makes no team: supervisor 'Odoo' needs a `ref` into the agent catalogue, an `app` from the application catalogue, or a `model` and `instructions` of its own"
22
+ ]
23
+ },
24
+ {
25
+ "name": "a system asks",
26
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n - member: odoo\n server: odoo-accounting\n persona:\n name: Odoo\n talks_to:\n - member: sales\n over: a2a\n",
27
+ "says": [
28
+ "Its cast makes no team: member 'odoo' is a server: it answers over MCP and asks nobody"
29
+ ]
30
+ },
31
+ {
32
+ "name": "a member talks to itself",
33
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n talks_to:\n - member: sales\n over: a2a\n",
34
+ "says": ["Its cast makes no team: member 'sales' talks to itself"]
35
+ },
36
+ {
37
+ "name": "a member talks to nobody of the cast",
38
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n talks_to:\n - member: ghost\n over: a2a\n",
39
+ "says": [
40
+ "Its cast makes no team: member 'sales' talks to 'ghost', which is not a member of team 'probe'"
41
+ ]
42
+ },
43
+ {
44
+ "name": "a system asked over a2a",
45
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n talks_to:\n - member: odoo\n over: a2a\n - member: odoo\n server: odoo-accounting\n persona:\n name: Odoo\n",
46
+ "says": [
47
+ "Its cast makes no team: member 'sales' talks to 'odoo' over a2a, which is a server: a server is asked over mcp"
48
+ ]
49
+ },
50
+ {
51
+ "name": "an agent asked over mcp",
52
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n talks_to:\n - member: books\n over: mcp\n - member: books\n app: accounting\n role: contributor\n runs_in: runtime\n persona:\n name: Accounting\n",
53
+ "says": [
54
+ "Its cast makes no team: member 'sales' talks to 'books' over mcp, which is not a server: an agent or an application is asked over a2a"
55
+ ]
56
+ },
57
+ {
58
+ "name": "a member named twice",
59
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n",
60
+ "says": ["probe: the cast names 'sales' twice"]
61
+ },
62
+ {
63
+ "name": "two beats with one name",
64
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\nscript:\n - id: open\n cue:\n say: Show the books\n expect: The books are read\n moves:\n - who: sales\n answers: words\n - id: open\n cue:\n say: Show them again\n expect: The books are read again\n moves:\n - who: sales\n answers: words\n",
65
+ "says": ["probe: the script has two beats named 'open'"]
66
+ },
67
+ {
68
+ "name": "a member placed outside the box",
69
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\nstage:\n positions:\n sales:\n x: 40\n y: 0.5\n",
70
+ "says": [
71
+ "probe: stage.positions.sales.x: Input should be less than or equal to 1"
72
+ ]
73
+ },
74
+ {
75
+ "name": "a member that is nothing at all",
76
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n - member: ghost\n",
77
+ "says": [
78
+ "Its cast makes no team: member 'ghost' needs a `ref` into the agent catalogue, an `app` from the application catalogue, a `server` from the MCP server catalogue, or enough of its own definition (`name`, `goal`) to stand alone"
79
+ ]
80
+ },
81
+ {
82
+ "name": "the scene wears a member's face",
83
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nemoji: 🧮\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n face: 🧮\n",
84
+ "says": [
85
+ "The scene wears 🧮, a member's face: a scene has a face of its own."
86
+ ]
87
+ },
88
+ {
89
+ "name": "a system of the setting that is no server",
90
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\nsetting:\n systems:\n - server: nosuch\n as: Nope\n",
91
+ "says": ["There is no MCP server named 'nosuch'."]
92
+ },
93
+ {
94
+ "name": "a system nobody reaches",
95
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\nsetting:\n systems:\n - server: odoo-accounting\n as: Odoo\n",
96
+ "says": [
97
+ "The system 'Odoo' (odoo-accounting) is on stage, and no member of the cast reaches it."
98
+ ]
99
+ },
100
+ {
101
+ "name": "a system reached through a connection",
102
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: books\ncast:\n - member: books\n app: accounting\n role: contributor\n runs_in: runtime\n persona:\n name: Accounting\nsetting:\n systems:\n - server: odoo-accounting\n as: Odoo\n",
103
+ "says": []
104
+ },
105
+ {
106
+ "name": "a Frame the catalogue has not",
107
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\nsetting:\n frames:\n - nosuch\n",
108
+ "says": ["There is no Frame named 'nosuch'."]
109
+ },
110
+ {
111
+ "name": "the stage places someone not in the cast",
112
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\nstage:\n positions:\n ghost:\n x: 0.2\n y: 0.4\n",
113
+ "says": ["The stage places 'ghost', which is not in the cast."]
114
+ },
115
+ {
116
+ "name": "a balloon opens first for someone not in the cast",
117
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\nstage:\n opens_first: ghost\n",
118
+ "says": ["The balloon of 'ghost' opens first, and it is not in the cast."]
119
+ },
120
+ {
121
+ "name": "an address for someone not in the cast",
122
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\ndeployment:\n addresses:\n ghost: GHOST_URL\n",
123
+ "says": ["An address is read for 'ghost', which is not in the cast."]
124
+ },
125
+ {
126
+ "name": "an address for a member in the browser",
127
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: browser\n persona:\n name: Sales\ndeployment:\n addresses:\n sales: SALES_URL\n",
128
+ "says": ["'sales' runs in the browser: it has no address."]
129
+ },
130
+ {
131
+ "name": "visitors may watch and a member has no address",
132
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\naudience:\n who: visitors\n",
133
+ "says": [
134
+ "Visitors may watch, and 'sales' runs on a runtime with no address: deployment.addresses names none for it."
135
+ ]
136
+ },
137
+ {
138
+ "name": "a beat its entry does not answer",
139
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n - member: books\n app: accounting\n role: contributor\n runs_in: runtime\n persona:\n name: Accounting\nscript:\n - id: open\n cue:\n say: Show the books\n expect: The books are read\n moves:\n - who: books\n answers: table\n",
140
+ "says": [
141
+ "Beat 'open': its cue is answered by nobody — no move is the entry's ('sales')."
142
+ ]
143
+ },
144
+ {
145
+ "name": "a beat moved by someone not in the cast",
146
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\nscript:\n - id: open\n cue:\n say: Show the books\n expect: The books are read\n moves:\n - who: sales\n answers: words\n - who: ghost\n answers: words\n",
147
+ "says": ["Beat 'open': 'ghost' moves, and is not in the cast."]
148
+ },
149
+ {
150
+ "name": "a beat moved by a system",
151
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n - member: odoo\n server: odoo-accounting\n persona:\n name: Odoo\nscript:\n - id: open\n cue:\n say: Show the books\n expect: The books are read\n moves:\n - who: sales\n answers: words\n - who: odoo\n answers: words\n",
152
+ "says": [
153
+ "Beat 'open': 'odoo' moves, and a system answers tool by tool: it asks nobody."
154
+ ]
155
+ },
156
+ {
157
+ "name": "a move that asks someone not in the cast",
158
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\nscript:\n - id: open\n cue:\n say: Show the books\n expect: The books are read\n moves:\n - who: sales\n asks: ghost\n over: a2a\n what: the books\n",
159
+ "says": ["Beat 'open': 'sales' asks 'ghost', which is not in the cast."]
160
+ },
161
+ {
162
+ "name": "a move over mcp that asks no system",
163
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n - member: books\n app: accounting\n role: contributor\n runs_in: runtime\n persona:\n name: Accounting\nscript:\n - id: open\n cue:\n say: Show the books\n expect: The books are read\n moves:\n - who: sales\n asks: books\n over: mcp\n what: the books\n",
164
+ "says": [
165
+ "Beat 'open': 'sales' asks 'books' over mcp, which is not a system of the scene."
166
+ ]
167
+ },
168
+ {
169
+ "name": "a move over a2a that asks a system",
170
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n - member: odoo\n server: odoo-accounting\n persona:\n name: Odoo\nscript:\n - id: open\n cue:\n say: Show the books\n expect: The books are read\n moves:\n - who: sales\n asks: odoo\n over: a2a\n what: the books\n",
171
+ "says": [
172
+ "Beat 'open': 'sales' asks 'odoo' over a2a, a system: a system is asked over mcp."
173
+ ]
174
+ },
175
+ {
176
+ "name": "a move that asks a member it does not talk to",
177
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n - member: books\n app: accounting\n role: contributor\n runs_in: runtime\n persona:\n name: Accounting\nscript:\n - id: open\n cue:\n say: Show the books\n expect: The books are read\n moves:\n - who: sales\n asks: books\n over: a2a\n what: the books\n",
178
+ "says": [
179
+ "Beat 'open': 'sales' asks 'books', which the team does not have it talk to."
180
+ ]
181
+ },
182
+ {
183
+ "name": "a branch that goes on to no beat",
184
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\nscript:\n - id: open\n cue:\n say: Show the books\n expect: The books are read\n moves:\n - who: sales\n answers: words\n branch:\n - decision: there is nothing to read\n expect: it says so\n then: nowhere\n",
185
+ "says": [
186
+ "Beat 'open': its branch goes on to 'nowhere', which is no beat."
187
+ ]
188
+ },
189
+ {
190
+ "name": "a rehearsal of a beat that is not in the script",
191
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\nrehearsal:\n beats:\n - beat: nowhere\n lines:\n - 'You → Sales'\n",
192
+ "says": [
193
+ "The rehearsal names the beat 'nowhere', which is not in the script."
194
+ ]
195
+ },
196
+ {
197
+ "name": "a rehearsal that names a beat twice",
198
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\nscript:\n - id: open\n cue:\n say: Show the books\n expect: The books are read\n moves:\n - who: sales\n answers: words\nrehearsal:\n beats:\n - beat: open\n lines:\n - 'You → Sales'\n - beat: open\n lines:\n - 'Sales: a table'\n",
199
+ "says": ["The rehearsal names the beat 'open' twice."]
200
+ },
201
+ {
202
+ "name": "a rehearsal that names someone not on stage",
203
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\nscript:\n - id: open\n cue:\n say: Show the books\n expect: The books are read\n moves:\n - who: sales\n answers: words\nrehearsal:\n beats:\n - beat: open\n lines:\n - 'You → Stranger'\n",
204
+ "says": [
205
+ "The rehearsal of 'open' names 'Stranger', which is not on stage."
206
+ ]
207
+ },
208
+ {
209
+ "name": "a scene that plays",
210
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n talks_to:\n - member: books\n over: a2a\n - member: books\n app: accounting\n role: contributor\n runs_in: runtime\n persona:\n name: Accounting\nsetting:\n systems:\n - server: odoo-accounting\n as: Odoo\n holds: Datalayer's books\nstage:\n positions:\n sales:\n x: 0\n y: 0\n books:\n x: 0.5\n y: 0\n opens_first: sales\nscript:\n - id: open\n cue:\n say: Show the books\n expect: The books are read\n moves:\n - who: sales\n asks: books\n over: a2a\n what: the open invoices\n - who: sales\n answers: table\nrehearsal:\n beats:\n - beat: open\n lines:\n - 'You → Sales'\n - 'Sales → Accounting'\n - 'Sales: a table'\n",
211
+ "says": []
212
+ },
213
+ {
214
+ "name": "a scene of a team that plays",
215
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nteam: sales-and-accounting\ncast:\n - member: sales\n persona:\n name: Sales\n brief: Ask for the open invoices.\n",
216
+ "says": []
217
+ },
218
+ {
219
+ "name": "a cast under a team names someone not in it",
220
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nteam: sales-and-accounting\ncast:\n - member: ghost\n persona:\n name: Ghost\n",
221
+ "says": [
222
+ "The cast names 'ghost', which is not a member of the team 'sales-and-accounting'."
223
+ ]
224
+ },
225
+ {
226
+ "name": "a cast under a team says what a member is",
227
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nteam: sales-and-accounting\ncast:\n - member: sales\n app: sales\n persona:\n name: Sales\n",
228
+ "says": [
229
+ "The cast member 'sales' is the team's: it says its persona and its brief, nothing of what it is."
230
+ ]
231
+ },
232
+ {
233
+ "name": "a move asks a system for a tool it does not offer",
234
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: books\ncast:\n - member: books\n app: accounting\n role: contributor\n runs_in: runtime\n persona:\n name: Accounting\nsetting:\n systems:\n - server: odoo-accounting\n as: Odoo\nscript:\n - id: open\n cue:\n say: Show the books\n expect: It is answered\n moves:\n - who: books\n asks: odoo-accounting\n over: mcp\n tool: odoo_accounting_burn_the_books\n",
235
+ "says": [
236
+ "Beat 'open': 'books' asks 'odoo-accounting' for 'odoo_accounting_burn_the_books', which odoo-accounting does not offer."
237
+ ]
238
+ },
239
+ {
240
+ "name": "a move asks for a tool to do what it does not",
241
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: crop\ncast:\n - member: crop\n app: crop-monitoring\n role: contributor\n runs_in: runtime\n persona:\n name: Crop\nsetting:\n systems:\n - server: earthdata\n as: Odoo\nscript:\n - id: look\n cue:\n say: What is there\n expect: It is answered\n moves:\n - who: crop\n asks: earthdata\n over: mcp\n tool: download_earth_data_granules\n does: read\n",
242
+ "says": [
243
+ "Beat 'look': 'crop' asks 'earthdata' for 'download_earth_data_granules' to read, and it writes."
244
+ ]
245
+ },
246
+ {
247
+ "name": "a move asks for a tool no connection of the member offers",
248
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: inbox\ncast:\n - member: inbox\n app: inbox-triage\n role: contributor\n runs_in: runtime\n persona:\n name: Inbox\nsetting:\n systems:\n - server: google-workspace\n as: Odoo\nscript:\n - id: mail\n cue:\n say: What came in\n expect: It is answered\n moves:\n - who: inbox\n asks: google-workspace\n over: mcp\n tool: list_calendar_events\n",
249
+ "says": [
250
+ "Beat 'mail': 'inbox' asks 'google-workspace' for 'list_calendar_events', a tool no connection of Inbox offers."
251
+ ]
252
+ },
253
+ {
254
+ "name": "a rehearsal expects a tool a system does not offer",
255
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: books\ncast:\n - member: books\n app: accounting\n role: contributor\n runs_in: runtime\n persona:\n name: Accounting\nsetting:\n systems:\n - server: odoo-accounting\n as: Odoo\nscript:\n - id: open\n cue:\n say: Show the books\n expect: It is answered\n moves:\n - who: books\n answers: table\nrehearsal:\n beats:\n - beat: open\n lines:\n - 'Accounting → Odoo: odoo_accounting_burn_the_books'\n - 'Accounting: a table'\n",
256
+ "says": [
257
+ "The rehearsal of 'open' expects 'odoo_accounting_burn_the_books', which odoo-accounting does not offer."
258
+ ]
259
+ },
260
+ {
261
+ "name": "a cast that is a word",
262
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast: sales\n",
263
+ "says": ["probe: cast: Input should be a valid list"]
264
+ },
265
+ {
266
+ "name": "a name that is a number",
267
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: 123\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n",
268
+ "says": ["probe: name: Input should be a valid string"]
269
+ },
270
+ {
271
+ "name": "a scene with no name",
272
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n",
273
+ "says": ["probe: name is missing"]
274
+ },
275
+ {
276
+ "name": "a field the spec does not have",
277
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\ncolour: red\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n",
278
+ "says": ["probe: colour is not a field of the spec"]
279
+ },
280
+ {
281
+ "name": "a member with a field it does not have",
282
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\n mood: happy\n",
283
+ "says": ["probe: cast.0.mood is not a field of the spec"]
284
+ },
285
+ {
286
+ "name": "a member running nowhere",
287
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: nowhere\n persona:\n name: Sales\n",
288
+ "says": ["probe: cast.0.runs_in: Input should be 'browser' or 'runtime'"]
289
+ },
290
+ {
291
+ "name": "a member's role nobody plays",
292
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: boss\n runs_in: runtime\n persona:\n name: Sales\n",
293
+ "says": [
294
+ "probe: cast.0.role: Input should be 'coordinator', 'initiator', 'contributor', 'reviewer' or 'finalizer'"
295
+ ]
296
+ },
297
+ {
298
+ "name": "an audience that is a word",
299
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\naudience: everyone\n",
300
+ "says": [
301
+ "probe: audience: Input should be a valid dictionary or instance of SceneAudience"
302
+ ]
303
+ },
304
+ {
305
+ "name": "an audience nobody can be",
306
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\naudience:\n who: nobody-at-all\n",
307
+ "says": [
308
+ "probe: audience.who: Input should be 'visitors', 'signed-in' or 'nobody'"
309
+ ]
310
+ },
311
+ {
312
+ "name": "a ceiling below nothing",
313
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\naudience:\n ceiling_per_ask: -1\n",
314
+ "says": [
315
+ "probe: audience.ceiling_per_ask: Input should be greater than or equal to 0"
316
+ ]
317
+ },
318
+ {
319
+ "name": "a ceiling that is no number",
320
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\naudience:\n ceiling_per_ask: lots\n",
321
+ "says": [
322
+ "probe: audience.ceiling_per_ask: Input should be a valid number, unable to parse string as a number"
323
+ ]
324
+ },
325
+ {
326
+ "name": "a rehearsal bound that is no duration",
327
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\nrehearsal:\n within: soon\n",
328
+ "says": [
329
+ "probe: rehearsal.within: String should match pattern '^(?:[1-9]\\d*(?:ms|s|m|h))?$'"
330
+ ]
331
+ },
332
+ {
333
+ "name": "a stage that is a list",
334
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\nstage: []\n",
335
+ "says": [
336
+ "probe: stage: Input should be a valid dictionary or instance of SceneStage"
337
+ ]
338
+ },
339
+ {
340
+ "name": "places that are a word",
341
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast:\n - member: sales\n app: sales\n role: initiator\n runs_in: runtime\n persona:\n name: Sales\nstage:\n positions: everywhere\n",
342
+ "says": ["probe: stage.positions: Input should be a valid dictionary"]
343
+ },
344
+ {
345
+ "name": "two things wrong at once",
346
+ "yaml": "schema: loop.scene/v1\nid: probe\nversion: 0.0.1\nname: Probe\nentry: sales\ncast: sales\nrehearsal:\n within: soon\n",
347
+ "says": [
348
+ "probe: cast: Input should be a valid list; rehearsal.within: String should match pattern '^(?:[1-9]\\d*(?:ms|s|m|h))?$'"
349
+ ]
350
+ }
351
+ ]
352
+ }
@@ -0,0 +1,92 @@
1
+ import type { SceneSpec } from '../../types/scenes';
2
+ /** The part of a scene a refusal is about: the sections of its spec. */
3
+ export type SceneSection = 'scene' | 'cast' | 'setting' | 'script' | 'stage' | 'audience' | 'rehearsal';
4
+ /** One refusal: the sentence agentspecs says, and the section it is about. */
5
+ export type SceneProblem = {
6
+ says: string;
7
+ section: SceneSection;
8
+ };
9
+ /**
10
+ * The sections a stage is written in (S-09): where each player runs and
11
+ * what it is given (`stage`, `deployment`, a member's `runs_in`), and what
12
+ * the audience may do (`audience`).
13
+ */
14
+ export declare const STAGE_SECTIONS: readonly SceneSection[];
15
+ /**
16
+ * The parts a person reads one at a time in the spec editor (S-08, S-09),
17
+ * in the order a scene is written. The scene's own fields are always in
18
+ * view, so they are not one of them.
19
+ */
20
+ export declare const SCENE_PARTS: readonly SceneSection[];
21
+ /**
22
+ * Each section of a scene, and the keys of the spec it is written in.
23
+ * *Where it plays* is the stage directions and the addresses together: where
24
+ * each player runs is one thing, said in two keys. Read by the editor's
25
+ * parts (S-09) and by `sceneShape`, which files a refusal of the scene's
26
+ * shape under the part whose key it names.
27
+ */
28
+ export declare const SCENE_SECTION_KEYS: Readonly<Record<SceneSection, readonly string[]>>;
29
+ /** The section a key of the spec is written in; the scene's own otherwise. */
30
+ export declare const sectionOfKey: (key: string) => SceneSection;
31
+ /** What a person reads each section as, in the editor's panel. */
32
+ export declare const SECTION_WORDS: Record<SceneSection, string>;
33
+ /**
34
+ * The checks that are not the browser's: made by agentspecs when `loop`
35
+ * loads the scene. Said here so that no second set of them is written.
36
+ */
37
+ export declare const AGENTSPECS_OWN: readonly string[];
38
+ /** The entry agentspecs reads: the one said, else the initiator, else the first of the cast. */
39
+ export declare function entryOf(spec: SceneSpec): string;
40
+ /**
41
+ * One of the person's own applications, kept in their Space: what the checks
42
+ * read of it when a member of the cast is one of them rather than an
43
+ * application of the catalogue — its name and face, and what its connections
44
+ * reach. agentspecs reads the catalogue only, so to `loop` a member that is
45
+ * the person's own application reaches no system at all; the Studio has the
46
+ * person's applications, and judges them by what they truly reach, in the
47
+ * same sentences (decided 2026-10-10, S-10).
48
+ */
49
+ export type SceneOwnApp = {
50
+ name: string;
51
+ emoji?: string;
52
+ connections: readonly {
53
+ server: string;
54
+ only?: readonly string[];
55
+ }[];
56
+ };
57
+ /** The person's own applications, by uid — the reference a scene's cast writes for one. */
58
+ export type SceneOwnApps = Readonly<Record<string, SceneOwnApp>>;
59
+ /**
60
+ * A line of a rehearsal's shape, as agentspecs reads it (`parse_line`):
61
+ * *Sales → Accounting* (an ask), *Accounting → Odoo: odoo_*\** (a tool),
62
+ * *Accounting: a table* (an answer).
63
+ */
64
+ export declare function transcriptLineOf(text: string): {
65
+ who: string;
66
+ whom: string;
67
+ detail: string;
68
+ } | undefined;
69
+ /**
70
+ * What is wrong with the shape of the scene, as agentspecs reads it
71
+ * (`SceneSpec._stages_somebody`), said as `parse_scene` says it — the
72
+ * scene's id, then the sentence. The first stops, as the reader stops.
73
+ */
74
+ export declare function sceneShapeProblem(spec: SceneSpec): SceneProblem | undefined;
75
+ /**
76
+ * What stops a scene from being played, each in agentspecs' sentence with
77
+ * the section it is about; empty when nothing does.
78
+ */
79
+ export declare function sceneCheck(spec: SceneSpec, own?: SceneOwnApps, { written, }?: {
80
+ /**
81
+ * Whether the spec is what a person wrote, read from its text. A cast
82
+ * under a team is judged on what was written, and only a text says that:
83
+ * the catalogue's scenes reach the browser with every member filled in
84
+ * from its team (`cast_of`), so read from there a valid scene would be
85
+ * refused for what its team said (seen 2026-10-10).
86
+ */
87
+ written?: boolean;
88
+ }): SceneProblem[];
89
+ /** What stops a scene from being played, in agentspecs' sentences; empty when nothing does. */
90
+ export declare const sceneProblems: (spec: SceneSpec, own?: SceneOwnApps) => string[];
91
+ /** The refusals of one section of the spec: the stage's, for S-09's own panel. */
92
+ export declare const problemsOfSections: (problems: readonly SceneProblem[], sections: readonly SceneSection[]) => SceneProblem[];