@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.
Files changed (74) hide show
  1. package/README.md +29 -297
  2. package/dist-embed/datalayer-app-main.js +2 -2
  3. package/lib/components/teams/A2ATeamGraph.d.ts +40 -155
  4. package/lib/components/teams/A2ATeamGraph.js +122 -400
  5. package/lib/components/teams/SceneTranscript.d.ts +5 -1
  6. package/lib/components/teams/SceneTranscript.js +2 -2
  7. package/lib/components/teams/SceneView.d.ts +17 -5
  8. package/lib/components/teams/SceneView.js +7 -3
  9. package/lib/components/teams/backdrops/catalogue.d.ts +35 -0
  10. package/lib/components/teams/backdrops/catalogue.js +34 -0
  11. package/lib/components/teams/backdrops/edges.d.ts +20 -0
  12. package/lib/components/teams/backdrops/edges.js +29 -0
  13. package/lib/components/teams/backdrops/faces.d.ts +24 -0
  14. package/lib/components/teams/backdrops/faces.js +39 -0
  15. package/lib/components/teams/backdrops/halo.d.ts +14 -0
  16. package/lib/components/teams/backdrops/halo.js +5 -0
  17. package/lib/components/teams/backdrops/hero.d.ts +14 -0
  18. package/lib/components/teams/backdrops/hero.js +178 -0
  19. package/lib/components/teams/backdrops/index.d.ts +22 -0
  20. package/lib/components/teams/backdrops/index.js +38 -0
  21. package/lib/components/teams/backdrops/nodes.d.ts +123 -0
  22. package/lib/components/teams/backdrops/nodes.js +265 -0
  23. package/lib/components/teams/backdrops/plain.d.ts +14 -0
  24. package/lib/components/teams/backdrops/plain.js +116 -0
  25. package/lib/components/teams/backdrops/studio.d.ts +17 -0
  26. package/lib/components/teams/backdrops/studio.js +122 -0
  27. package/lib/components/teams/backdrops/theme.d.ts +14 -0
  28. package/lib/components/teams/backdrops/theme.js +19 -0
  29. package/lib/components/teams/backdrops/types.d.ts +214 -0
  30. package/lib/components/teams/backdrops/types.js +5 -0
  31. package/lib/components/teams/index.d.ts +2 -0
  32. package/lib/components/teams/index.js +2 -0
  33. package/lib/components/teams/sceneTranscriptRules.d.ts +32 -1
  34. package/lib/components/teams/sceneTranscriptRules.js +104 -2
  35. package/lib/components/teams/transcript/RecordingLabel.d.ts +17 -0
  36. package/lib/components/teams/transcript/RecordingLabel.js +7 -0
  37. package/lib/components/teams/transcript/SequenceDiagram.d.ts +39 -0
  38. package/lib/components/teams/transcript/SequenceDiagram.js +190 -0
  39. package/lib/components/teams/transcript/TranscriptDetail.d.ts +27 -0
  40. package/lib/components/teams/transcript/TranscriptDetail.js +48 -0
  41. package/lib/components/teams/transcript/TranscriptTable.d.ts +23 -0
  42. package/lib/components/teams/transcript/TranscriptTable.js +112 -0
  43. package/lib/components/teams/transcript/TranscriptViews.d.ts +66 -0
  44. package/lib/components/teams/transcript/TranscriptViews.js +135 -0
  45. package/lib/components/teams/transcript/TranscriptViewsTypes.d.ts +13 -0
  46. package/lib/components/teams/transcript/TranscriptViewsTypes.js +5 -0
  47. package/lib/components/teams/transcript/index.d.ts +23 -0
  48. package/lib/components/teams/transcript/index.js +27 -0
  49. package/lib/components/teams/transcript/recordingPlayback.d.ts +52 -0
  50. package/lib/components/teams/transcript/recordingPlayback.js +146 -0
  51. package/lib/components/teams/transcript/selection.d.ts +59 -0
  52. package/lib/components/teams/transcript/selection.js +79 -0
  53. package/lib/components/teams/transcript/sequenceDiagramRules.d.ts +35 -0
  54. package/lib/components/teams/transcript/sequenceDiagramRules.js +158 -0
  55. package/lib/components/teams/transcript/transcriptDetailRules.d.ts +80 -0
  56. package/lib/components/teams/transcript/transcriptDetailRules.js +208 -0
  57. package/lib/components/teams/transcript/transcriptExport.d.ts +31 -0
  58. package/lib/components/teams/transcript/transcriptExport.js +82 -0
  59. package/lib/components/teams/transcript/transcriptRows.d.ts +76 -0
  60. package/lib/components/teams/transcript/transcriptRows.js +187 -0
  61. package/lib/components/teams/transcript/useSceneRecording.d.ts +45 -0
  62. package/lib/components/teams/transcript/useSceneRecording.js +96 -0
  63. package/lib/examples/ScenesExample.js +16 -3
  64. package/lib/specs/actions.js +2 -0
  65. package/lib/specs/apps.d.ts +1 -0
  66. package/lib/specs/apps.js +219 -0
  67. package/lib/specs/sceneRecordings.d.ts +14 -0
  68. package/lib/specs/sceneRecordings.js +12 -0
  69. package/lib/specs/sceneSchema.js +11 -0
  70. package/lib/specs/scenes.d.ts +1 -0
  71. package/lib/specs/scenes.js +159 -0
  72. package/lib/types/scenes.d.ts +54 -0
  73. package/package.json +2 -1
  74. package/scripts/codegen/generate_scenes.py +80 -0
@@ -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,
@@ -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.90",
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)