@datalayer/agent-runtimes 1.5.90 → 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 +2 -2
- package/lib/components/teams/A2ATeamGraph.d.ts +40 -155
- package/lib/components/teams/A2ATeamGraph.js +122 -400
- 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/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/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_scenes.py +80 -0
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,
|
package/lib/types/scenes.d.ts
CHANGED
|
@@ -144,7 +144,17 @@ export interface SceneStageSpec {
|
|
|
144
144
|
/** How long the scene plays before it rests, `10m`. */
|
|
145
145
|
restsAfter: string;
|
|
146
146
|
pace: ScenePace;
|
|
147
|
+
/**
|
|
148
|
+
* How the stage is drawn, here and wherever the scene plays: `studio`,
|
|
149
|
+
* characters with speech balloons and each message flowing along its
|
|
150
|
+
* line; `hero`, round faces on a deep tint, thin lines and a dot that
|
|
151
|
+
* flies with each message; `plain`, boxes and arrows that do not move.
|
|
152
|
+
* `studio` when unsaid, so every scene written before draws as before.
|
|
153
|
+
*/
|
|
154
|
+
backdrop?: SceneBackdropId;
|
|
147
155
|
}
|
|
156
|
+
/** A backdrop, by its id (agentspecs `Backdrop`). */
|
|
157
|
+
export type SceneBackdropId = 'studio' | 'hero' | 'plain';
|
|
148
158
|
/** Who may watch and ask, and what an ask may cost. */
|
|
149
159
|
export interface SceneAudienceSpec {
|
|
150
160
|
who: 'visitors' | 'signed-in' | 'nobody';
|
|
@@ -168,6 +178,50 @@ export interface SceneRecordingSpec {
|
|
|
168
178
|
taken: string;
|
|
169
179
|
note: string;
|
|
170
180
|
}
|
|
181
|
+
/** A line of a recording: the transcript's line as a stage exports it (`SceneTranscriptLine`). */
|
|
182
|
+
export interface SceneRecordedLine {
|
|
183
|
+
key: string;
|
|
184
|
+
/** When, in milliseconds since the epoch. */
|
|
185
|
+
at: number;
|
|
186
|
+
from: string;
|
|
187
|
+
to?: string;
|
|
188
|
+
kind: 'said' | 'asked' | 'called';
|
|
189
|
+
text: string;
|
|
190
|
+
open?: boolean;
|
|
191
|
+
failed?: boolean;
|
|
192
|
+
ended?: number;
|
|
193
|
+
}
|
|
194
|
+
/** A beat of a recording: which of its lines it played (`first`, `count`). */
|
|
195
|
+
export interface SceneRecordedBeat {
|
|
196
|
+
beat: string;
|
|
197
|
+
cue: string;
|
|
198
|
+
state: string;
|
|
199
|
+
seconds: number;
|
|
200
|
+
first: number;
|
|
201
|
+
count: number;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* A scene's recording, as `<id>/recording.json` holds it: the transcript of
|
|
205
|
+
* a passing rehearsal (version 1, the JSON a stage exports), when it was
|
|
206
|
+
* taken, the rehearsal it came from and the beats its lines belong to.
|
|
207
|
+
*/
|
|
208
|
+
export interface SceneRecordingFile {
|
|
209
|
+
version: 1;
|
|
210
|
+
scene: string;
|
|
211
|
+
/** When it was taken: the label says *Recording, taken <date>*. */
|
|
212
|
+
taken: string;
|
|
213
|
+
/** What the audience is told of it. */
|
|
214
|
+
note?: string;
|
|
215
|
+
exported?: string;
|
|
216
|
+
rehearsal?: {
|
|
217
|
+
at: string;
|
|
218
|
+
where: string;
|
|
219
|
+
passed: boolean;
|
|
220
|
+
says: string;
|
|
221
|
+
};
|
|
222
|
+
beats?: SceneRecordedBeat[];
|
|
223
|
+
lines: SceneRecordedLine[];
|
|
224
|
+
}
|
|
171
225
|
/** The scene's tests: a passing rehearsal is the gallery's *Live*. */
|
|
172
226
|
export interface SceneRehearsalSpec {
|
|
173
227
|
beats: SceneRehearsalBeatSpec[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datalayer/agent-runtimes",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.91",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"workspaces": [
|
|
6
6
|
".",
|
|
@@ -254,6 +254,7 @@
|
|
|
254
254
|
"localforage": "^1.10.0",
|
|
255
255
|
"lucide-react": "^0.542.0",
|
|
256
256
|
"marked": "^4.0.10",
|
|
257
|
+
"mermaid": "^10.9.8",
|
|
257
258
|
"mock-socket": "^9.3.1",
|
|
258
259
|
"motion": "^12.23.26",
|
|
259
260
|
"nanoid": "^5.1.6",
|
|
@@ -21,6 +21,12 @@ The scene spec's JSON Schema is written beside the TypeScript catalogue as
|
|
|
21
21
|
``generate_apps.py`` writes ``appspecSchema.ts``: what an editor of the text
|
|
22
22
|
completes and explains is what the spec accepts (S-01, A-03).
|
|
23
23
|
|
|
24
|
+
A scene's recording — the transcript of a passing rehearsal, kept beside
|
|
25
|
+
the specs as ``<id>/recording.json`` and named by ``rehearsal.recording`` —
|
|
26
|
+
is written to ``sceneRecordings.ts`` beside the catalogue, apart from it, so
|
|
27
|
+
that a page that never plays one never loads one: ``SCENE_RECORDINGS``, read
|
|
28
|
+
by ``useSceneRecording`` at the recorded pace.
|
|
29
|
+
|
|
24
30
|
Usage:
|
|
25
31
|
python generate_scenes.py \\
|
|
26
32
|
--specs-dir agentspecs/agentspecs/scenes \\
|
|
@@ -290,6 +296,76 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
290
296
|
return "\n".join(lines)
|
|
291
297
|
|
|
292
298
|
|
|
299
|
+
def load_recordings(specs_dir: Path) -> dict[str, dict[str, Any]]:
|
|
300
|
+
"""Every scene's recording its spec names, by the scene's id, as its file holds it.
|
|
301
|
+
|
|
302
|
+
A file the spec names that is not there, or is not a transcript of
|
|
303
|
+
version 1, stops the generation in a sentence (agentspecs'
|
|
304
|
+
``scene_recording``, when the clone has it).
|
|
305
|
+
"""
|
|
306
|
+
scenes_module = import_from_clone(specs_dir, "scenes")
|
|
307
|
+
read = getattr(scenes_module, "scene_recording", None)
|
|
308
|
+
recordings: dict[str, dict[str, Any]] = {}
|
|
309
|
+
for identity, scene in sorted(scenes_module.load_scenes(specs_dir).items()):
|
|
310
|
+
named = scene.rehearsal.recording
|
|
311
|
+
if named is None:
|
|
312
|
+
continue
|
|
313
|
+
path = specs_dir / named.path
|
|
314
|
+
if read is not None and path == specs_dir / identity / "recording.json":
|
|
315
|
+
found = read(identity, specs_dir)
|
|
316
|
+
else:
|
|
317
|
+
found = json.loads(path.read_text()) if path.is_file() else None
|
|
318
|
+
if not found:
|
|
319
|
+
raise SystemExit(
|
|
320
|
+
f"The recording of {identity} ({named.path}) is not there."
|
|
321
|
+
)
|
|
322
|
+
recordings[identity] = {
|
|
323
|
+
**found,
|
|
324
|
+
"scene": identity,
|
|
325
|
+
"taken": named.taken or found.get("taken") or "",
|
|
326
|
+
**({"note": named.note} if named.note else {}),
|
|
327
|
+
}
|
|
328
|
+
return recordings
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def generate_recordings_typescript_code(recordings: dict[str, dict[str, Any]]) -> str:
|
|
332
|
+
"""The scenes' recordings, apart from the catalogue: a page that plays none loads none."""
|
|
333
|
+
body = json.dumps(recordings, indent=2, ensure_ascii=False)
|
|
334
|
+
return "\n".join(
|
|
335
|
+
[
|
|
336
|
+
"/*",
|
|
337
|
+
" * Copyright (c) 2025-2026 Datalayer, Inc.",
|
|
338
|
+
" * Distributed under the terms of the Modified BSD License.",
|
|
339
|
+
" */",
|
|
340
|
+
"",
|
|
341
|
+
"/**",
|
|
342
|
+
" * The scenes' recordings: each the transcript of a passing rehearsal, kept",
|
|
343
|
+
" * beside its spec as `<id>/recording.json` (`loop scenes record`) and played",
|
|
344
|
+
" * at its recorded pace when the scene cannot play live (`useSceneRecording`).",
|
|
345
|
+
" *",
|
|
346
|
+
*[f" * {line}" for line in HEADER],
|
|
347
|
+
" *",
|
|
348
|
+
" * @module specs/sceneRecordings",
|
|
349
|
+
" */",
|
|
350
|
+
"",
|
|
351
|
+
"import type { SceneRecordingFile } from '../types/scenes';",
|
|
352
|
+
"",
|
|
353
|
+
f"export const SCENE_RECORDINGS: Record<string, SceneRecordingFile> = {body};",
|
|
354
|
+
"",
|
|
355
|
+
"/** A scene's recording, by its id or `id:version`; undefined when it has none. */",
|
|
356
|
+
"export function getSceneRecording(",
|
|
357
|
+
" ref: string,",
|
|
358
|
+
"): SceneRecordingFile | undefined {",
|
|
359
|
+
" const id = ref.split(':')[0];",
|
|
360
|
+
" return Object.prototype.hasOwnProperty.call(SCENE_RECORDINGS, id)",
|
|
361
|
+
" ? SCENE_RECORDINGS[id]",
|
|
362
|
+
" : undefined;",
|
|
363
|
+
"}",
|
|
364
|
+
"",
|
|
365
|
+
]
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
|
|
293
369
|
def generate_schema_typescript_code(specs_dir: Path) -> str:
|
|
294
370
|
"""The scene spec's JSON Schema, for the editor of its text (S-01, A-03).
|
|
295
371
|
|
|
@@ -344,6 +420,10 @@ def main() -> None:
|
|
|
344
420
|
args.typescript_output.with_name("sceneSchema.ts"),
|
|
345
421
|
generate_schema_typescript_code(args.specs_dir),
|
|
346
422
|
),
|
|
423
|
+
(
|
|
424
|
+
args.typescript_output.with_name("sceneRecordings.ts"),
|
|
425
|
+
generate_recordings_typescript_code(load_recordings(args.specs_dir)),
|
|
426
|
+
),
|
|
347
427
|
):
|
|
348
428
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
349
429
|
path.write_text(text)
|