agent-screenplay 0.1.0 → 0.1.2

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 (58) hide show
  1. package/README.md +27 -7
  2. package/assets/editor/index.html +143 -23
  3. package/dist/cli.js +3 -2
  4. package/dist/cli.js.map +1 -1
  5. package/dist/core.d.ts +2 -0
  6. package/dist/core.js +3 -15
  7. package/dist/core.js.map +1 -1
  8. package/dist/direct/plan.d.ts +2 -0
  9. package/dist/direct/plan.js +8 -0
  10. package/dist/direct/plan.js.map +1 -1
  11. package/dist/drivers/browser.d.ts +14 -0
  12. package/dist/drivers/browser.js +39 -1
  13. package/dist/drivers/browser.js.map +1 -1
  14. package/dist/drivers/web.d.ts +17 -0
  15. package/dist/drivers/web.js +80 -8
  16. package/dist/drivers/web.js.map +1 -1
  17. package/dist/editor/server.d.ts +28 -2
  18. package/dist/editor/server.js +39 -7
  19. package/dist/editor/server.js.map +1 -1
  20. package/dist/login.d.ts +9 -1
  21. package/dist/login.js +62 -29
  22. package/dist/login.js.map +1 -1
  23. package/dist/mcp/server.js +13 -3
  24. package/dist/mcp/server.js.map +1 -1
  25. package/dist/render/presets.d.ts +20 -3
  26. package/dist/render/presets.js +30 -3
  27. package/dist/render/presets.js.map +1 -1
  28. package/dist/render/render.d.ts +2 -0
  29. package/dist/render/render.js +5 -0
  30. package/dist/render/render.js.map +1 -1
  31. package/dist/render/style.d.ts +18 -5
  32. package/dist/render/style.js +124 -15
  33. package/dist/render/style.js.map +1 -1
  34. package/dist/scenario/config.d.ts +1 -0
  35. package/dist/scenario/fingerprint.js +2 -2
  36. package/dist/scenario/fingerprint.js.map +1 -1
  37. package/dist/scenario/schema.d.ts +43 -1
  38. package/dist/scenario/schema.js +25 -6
  39. package/dist/scenario/schema.js.map +1 -1
  40. package/dist/scenario/target.js +2 -1
  41. package/dist/scenario/target.js.map +1 -1
  42. package/dist/version.d.ts +1 -1
  43. package/dist/version.js +1 -1
  44. package/dist/voice/audio.d.ts +6 -0
  45. package/dist/voice/audio.js +36 -0
  46. package/dist/voice/audio.js.map +1 -0
  47. package/dist/voice/cartesia.d.ts +5 -0
  48. package/dist/voice/cartesia.js +17 -6
  49. package/dist/voice/cartesia.js.map +1 -1
  50. package/dist/voice/elevenlabs.d.ts +8 -0
  51. package/dist/voice/elevenlabs.js +29 -0
  52. package/dist/voice/elevenlabs.js.map +1 -0
  53. package/dist/voice/narration.d.ts +16 -0
  54. package/dist/voice/narration.js +83 -0
  55. package/dist/voice/narration.js.map +1 -0
  56. package/package.json +7 -3
  57. package/schema/scenario.schema.json +78 -6
  58. package/skills/screenplay/SKILL.md +21 -13
@@ -26,7 +26,7 @@
26
26
  "type": "boolean"
27
27
  },
28
28
  "viewport": {
29
- "description": "Web: browser size in CSS pixels (default 1440x900).",
29
+ "description": "Web: browser size in CSS pixels (default: the video's shape with a longer side of 1440, such as 1440x810 for 16:9, so the page fills the frame when padding is 0).",
30
30
  "type": "object",
31
31
  "properties": {
32
32
  "width": {
@@ -90,7 +90,7 @@
90
90
  "type": "object",
91
91
  "properties": {
92
92
  "background": {
93
- "description": "A preset (sunset, ocean, lavender, forest, midnight, mono, paper), a color like #0f172a, an image path (relative to the file that sets it), or a gradient { \"gradient\": [\"#0ea5e9\", \"#6366f1\"], \"angle\": 135 }.",
93
+ "description": "A preset (dawn, iris, citrus, apricot, coral, blaze, cloud, glacier, tide, meadow; default dawn), a color like #0f172a, an image path (relative to the file that sets it), or a gradient { \"gradient\": [\"#0ea5e9\", \"#6366f1\"], \"angle\": 135 }.",
94
94
  "anyOf": [
95
95
  {
96
96
  "type": "string"
@@ -178,6 +178,10 @@
178
178
  "none",
179
179
  "browser"
180
180
  ]
181
+ },
182
+ "cards": {
183
+ "description": "Show the title and end cards (default false: `title` and `end` steps are left out of the video). Turn on when the person wants an intro and an outro.",
184
+ "type": "boolean"
181
185
  }
182
186
  },
183
187
  "additionalProperties": false
@@ -186,15 +190,23 @@
186
190
  "type": "object",
187
191
  "properties": {
188
192
  "enabled": {
189
- "description": "Speak each step's `say` text (default false). Needs CARTESIA_API_KEY.",
193
+ "description": "Speak each step's `say` text (default false). Needs CARTESIA_API_KEY or ELEVENLABS_API_KEY, unless every spoken step has its own `audio` file.",
190
194
  "type": "boolean"
191
195
  },
196
+ "provider": {
197
+ "description": "Who speaks (default: Cartesia when CARTESIA_API_KEY is set, else ElevenLabs when ELEVENLABS_API_KEY is).",
198
+ "type": "string",
199
+ "enum": [
200
+ "cartesia",
201
+ "elevenlabs"
202
+ ]
203
+ },
192
204
  "language": {
193
205
  "description": "Language code, e.g. en, fr, de (default en).",
194
206
  "type": "string"
195
207
  },
196
208
  "id": {
197
- "description": "Cartesia voice id (default: a narration voice for the language).",
209
+ "description": "The provider's voice id (default: a narration voice).",
198
210
  "type": "string"
199
211
  },
200
212
  "speed": {
@@ -203,7 +215,7 @@
203
215
  "maximum": 1.5
204
216
  },
205
217
  "model": {
206
- "description": "Cartesia model (default sonic-3.6).",
218
+ "description": "The provider's model (default sonic-3.6 for Cartesia, eleven_multilingual_v2 for ElevenLabs).",
207
219
  "type": "string"
208
220
  }
209
221
  },
@@ -289,6 +301,10 @@
289
301
  }
290
302
  ]
291
303
  },
304
+ "audio": {
305
+ "description": "An audio file (MP3, WAV, M4A…) with this step's narration, relative to the scenario: used instead of generating `say`, with no voice key. The step lasts at least as long as the file.",
306
+ "type": "string"
307
+ },
292
308
  "hold": {
293
309
  "description": "Extra seconds to stay on the result of this step before the next one.",
294
310
  "type": "number",
@@ -341,6 +357,10 @@
341
357
  }
342
358
  ]
343
359
  },
360
+ "audio": {
361
+ "description": "An audio file (MP3, WAV, M4A…) with this step's narration, relative to the scenario: used instead of generating `say`, with no voice key. The step lasts at least as long as the file.",
362
+ "type": "string"
363
+ },
344
364
  "hold": {
345
365
  "description": "Extra seconds to stay on the result of this step before the next one.",
346
366
  "type": "number",
@@ -404,6 +424,10 @@
404
424
  }
405
425
  ]
406
426
  },
427
+ "audio": {
428
+ "description": "An audio file (MP3, WAV, M4A…) with this step's narration, relative to the scenario: used instead of generating `say`, with no voice key. The step lasts at least as long as the file.",
429
+ "type": "string"
430
+ },
407
431
  "hold": {
408
432
  "description": "Extra seconds to stay on the result of this step before the next one.",
409
433
  "type": "number",
@@ -473,6 +497,10 @@
473
497
  }
474
498
  ]
475
499
  },
500
+ "audio": {
501
+ "description": "An audio file (MP3, WAV, M4A…) with this step's narration, relative to the scenario: used instead of generating `say`, with no voice key. The step lasts at least as long as the file.",
502
+ "type": "string"
503
+ },
476
504
  "hold": {
477
505
  "description": "Extra seconds to stay on the result of this step before the next one.",
478
506
  "type": "number",
@@ -525,6 +553,10 @@
525
553
  }
526
554
  ]
527
555
  },
556
+ "audio": {
557
+ "description": "An audio file (MP3, WAV, M4A…) with this step's narration, relative to the scenario: used instead of generating `say`, with no voice key. The step lasts at least as long as the file.",
558
+ "type": "string"
559
+ },
528
560
  "hold": {
529
561
  "description": "Extra seconds to stay on the result of this step before the next one.",
530
562
  "type": "number",
@@ -578,6 +610,10 @@
578
610
  }
579
611
  ]
580
612
  },
613
+ "audio": {
614
+ "description": "An audio file (MP3, WAV, M4A…) with this step's narration, relative to the scenario: used instead of generating `say`, with no voice key. The step lasts at least as long as the file.",
615
+ "type": "string"
616
+ },
581
617
  "hold": {
582
618
  "description": "Extra seconds to stay on the result of this step before the next one.",
583
619
  "type": "number",
@@ -645,6 +681,10 @@
645
681
  }
646
682
  ]
647
683
  },
684
+ "audio": {
685
+ "description": "An audio file (MP3, WAV, M4A…) with this step's narration, relative to the scenario: used instead of generating `say`, with no voice key. The step lasts at least as long as the file.",
686
+ "type": "string"
687
+ },
648
688
  "hold": {
649
689
  "description": "Extra seconds to stay on the result of this step before the next one.",
650
690
  "type": "number",
@@ -701,6 +741,10 @@
701
741
  }
702
742
  ]
703
743
  },
744
+ "audio": {
745
+ "description": "An audio file (MP3, WAV, M4A…) with this step's narration, relative to the scenario: used instead of generating `say`, with no voice key. The step lasts at least as long as the file.",
746
+ "type": "string"
747
+ },
704
748
  "hold": {
705
749
  "description": "Extra seconds to stay on the result of this step before the next one.",
706
750
  "type": "number",
@@ -771,6 +815,10 @@
771
815
  }
772
816
  ]
773
817
  },
818
+ "audio": {
819
+ "description": "An audio file (MP3, WAV, M4A…) with this step's narration, relative to the scenario: used instead of generating `say`, with no voice key. The step lasts at least as long as the file.",
820
+ "type": "string"
821
+ },
774
822
  "hold": {
775
823
  "description": "Extra seconds to stay on the result of this step before the next one.",
776
824
  "type": "number",
@@ -825,6 +873,10 @@
825
873
  }
826
874
  ]
827
875
  },
876
+ "audio": {
877
+ "description": "An audio file (MP3, WAV, M4A…) with this step's narration, relative to the scenario: used instead of generating `say`, with no voice key. The step lasts at least as long as the file.",
878
+ "type": "string"
879
+ },
828
880
  "hold": {
829
881
  "description": "Extra seconds to stay on the result of this step before the next one.",
830
882
  "type": "number",
@@ -871,6 +923,10 @@
871
923
  "say": {
872
924
  "description": "Narration for this step: shown as a caption, and spoken when voice is enabled. The step lasts at least as long as the sentence.",
873
925
  "type": "string"
926
+ },
927
+ "audio": {
928
+ "description": "An audio file (MP3, WAV, M4A…) with this step's narration, relative to the scenario: used instead of generating `say`, with no voice key. The step lasts at least as long as the file.",
929
+ "type": "string"
874
930
  }
875
931
  },
876
932
  "required": [
@@ -896,6 +952,10 @@
896
952
  "say": {
897
953
  "description": "Narration for this step: shown as a caption, and spoken when voice is enabled. The step lasts at least as long as the sentence.",
898
954
  "type": "string"
955
+ },
956
+ "audio": {
957
+ "description": "An audio file (MP3, WAV, M4A…) with this step's narration, relative to the scenario: used instead of generating `say`, with no voice key. The step lasts at least as long as the file.",
958
+ "type": "string"
899
959
  }
900
960
  },
901
961
  "required": [
@@ -937,6 +997,10 @@
937
997
  }
938
998
  ]
939
999
  },
1000
+ "audio": {
1001
+ "description": "An audio file (MP3, WAV, M4A…) with this step's narration, relative to the scenario: used instead of generating `say`, with no voice key. The step lasts at least as long as the file.",
1002
+ "type": "string"
1003
+ },
940
1004
  "hold": {
941
1005
  "description": "Extra seconds to stay on the result of this step before the next one.",
942
1006
  "type": "number",
@@ -999,6 +1063,10 @@
999
1063
  }
1000
1064
  ]
1001
1065
  },
1066
+ "audio": {
1067
+ "description": "An audio file (MP3, WAV, M4A…) with this step's narration, relative to the scenario: used instead of generating `say`, with no voice key. The step lasts at least as long as the file.",
1068
+ "type": "string"
1069
+ },
1002
1070
  "hold": {
1003
1071
  "description": "Extra seconds to stay on the result of this step before the next one.",
1004
1072
  "type": "number",
@@ -1057,6 +1125,10 @@
1057
1125
  }
1058
1126
  ]
1059
1127
  },
1128
+ "audio": {
1129
+ "description": "An audio file (MP3, WAV, M4A…) with this step's narration, relative to the scenario: used instead of generating `say`, with no voice key. The step lasts at least as long as the file.",
1130
+ "type": "string"
1131
+ },
1060
1132
  "hold": {
1061
1133
  "description": "Extra seconds to stay on the result of this step before the next one.",
1062
1134
  "type": "number",
@@ -1093,6 +1165,6 @@
1093
1165
  "steps"
1094
1166
  ],
1095
1167
  "additionalProperties": false,
1096
- "$id": "https://raw.githubusercontent.com/gmpetrov/screenplay/main/schema/scenario.schema.json",
1168
+ "$id": "https://raw.githubusercontent.com/gmpetrov/agent-screenplay/main/schema/scenario.schema.json",
1097
1169
  "title": "Screenplay scenario"
1098
1170
  }
@@ -12,31 +12,29 @@ Tools: the MCP tools `screenplay_check`, `screenplay_record`, `screenplay_render
12
12
  ## The loop
13
13
 
14
14
  1. **Understand what to show.** One video shows one idea: a feature, a flow, a fix. 15 to 60 seconds. Decide the story first: the starting point, the 3 to 8 actions that matter, the result.
15
+ **Ask about the voice-over before writing**, in one question, unless the request already says: no voice (captions only, the default), or a voice-over, in which language. If they want one, see *Voice-over* below for how to get the voice.
15
16
  2. **Explore the app** (with your browser tools, or by reading its code) to find the real path: which page, which buttons, what text they carry, what data is on screen. Prefer a demo account with realistic data: an empty dashboard or "test123" ruins the video.
16
17
  3. **Write the scenario** next to where the video should go (for example `demos/handoff.json`).
17
18
  4. **Run `check`.** It plays the scenario fast, without filming, and tells you which step fails and which targets are visible instead. Fix and check again until it plays to the end.
18
19
  5. **Run `record`.** It films and edits, and writes `<name>.mp4` and `<name>.storyboard.png`.
19
20
  6. **Look at the storyboard** (one image per step). Check: is each zoom framed on the right element, is a bubble hiding something, does a step show an error, is the data credible? Fix the scenario. If you only changed text, narration, style or zoom, run `render` (no new filming); if you changed actions, `record` again.
20
- 7. Hand over the MP4 path (and the GIF if asked).
21
+ 7. Hand over the MP4 path (and the GIF if asked), then **offer to adjust the look**: the person can set the background, padding, corners, shadow and the intro/outro cards by eye with `npx agent-screenplay style <scenario.json>`, which opens an editor in their browser and re-renders without filming. If they accept and the computer has a screen, run it for them in the background (it keeps running until stopped) and say a browser tab opened; otherwise give them the command.
21
22
 
22
23
  ## Scenario
23
24
 
24
25
  ```json
25
26
  {
26
- "$schema": "https://raw.githubusercontent.com/gmpetrov/screenplay/main/schema/scenario.schema.json",
27
+ "$schema": "https://raw.githubusercontent.com/gmpetrov/agent-screenplay/main/schema/scenario.schema.json",
27
28
  "url": "https://app.example.com/inbox",
28
- "viewport": { "width": 1440, "height": 900 },
29
29
  "auth": { "storageState": ".screenplay/auth.json" },
30
- "style": { "background": "ocean" },
30
+ "style": { "background": "dawn" },
31
31
  "steps": [
32
- { "title": "Human handoff, in one click", "subtitle": "New in 2.4" },
33
32
  { "click": "text=Camille Laurent", "say": "When the AI agent gets stuck, the conversation lands in your inbox." },
34
33
  { "callout": "role=button[name='Take over']", "text": "Take over from the AI agent" },
35
34
  { "click": "role=button[name='Take over']" },
36
35
  { "type": "Hi Camille, I'm on it.", "into": "placeholder=Write a reply…" },
37
36
  { "click": "role=button[name='Send']" },
38
- { "highlight": "text=Assigned to you", "text": "Your teammates see it's yours" },
39
- { "end": "Available today on every plan" }
37
+ { "highlight": "text=Assigned to you", "text": "Your teammates see it's yours" }
40
38
  ]
41
39
  }
42
40
  ```
@@ -96,31 +94,41 @@ On any step: `say` (narration: a caption, and speech when voice is on), `zoom` (
96
94
  ### Style, voice, output
97
95
 
98
96
  ```json
99
- "style": { "background": "ocean", "padding": 72, "radius": 14, "cursor": "macos", "maxZoom": 1.9, "frame": "browser", "accent": "#3b82f6", "captions": true },
97
+ "style": { "background": "dawn", "padding": 72, "radius": 14, "cursor": "macos", "maxZoom": 1.9, "frame": "browser", "accent": "#3b82f6", "captions": true },
100
98
  "voice": { "enabled": true, "language": "fr" },
101
99
  "output": { "width": 1920, "height": 1080, "fps": 30, "gif": false }
102
100
  ```
103
101
 
104
- Backgrounds: `sunset`, `ocean`, `lavender`, `forest`, `midnight`, `mono`, `paper`, a color (`#0f172a`), an image path, or a gradient `{ "gradient": ["#0ea5e9", "#6366f1"], "angle": 135 }`. `frame: "browser"` adds a minimal browser bar with the URL.
102
+ Backgrounds: `dawn` (default), `iris`, `citrus`, `apricot`, `coral`, `blaze`, `cloud`, `glacier`, `tide`, `meadow`, a color (`#0f172a`), an image path, or a gradient `{ "gradient": ["#0ea5e9", "#6366f1"], "angle": 135 }`. `frame: "browser"` adds a minimal browser bar with the URL.
105
103
 
106
104
  A `screenplay.config.json` in the scenario's folder or a parent sets the style of every video below it (`{ "style": { … } }`); a scenario's own `style` wins, setting by setting. Keep a project's look there rather than repeating it in each scenario, and leave it alone unless asked. When a person wants to choose the look themselves, tell them to run `npx agent-screenplay style <scenario.json>`: it opens a visual editor in their browser (it needs a screen, so do not run it yourself in a headless environment).
107
105
 
108
- Voice-over is **off by default**. With `voice.enabled`, each `say` is spoken (Cartesia, needs `CARTESIA_API_KEY`) and the video waits for each sentence to finish. Without voice, `say` still shows as a caption.
106
+ Title and end cards are **off by default**: `title` and `end` steps are left out of the video unless `"style": { "cards": true }`. Add them, and turn cards on, only when the person asks for an intro or an outro.
107
+
108
+ ### Voice-over
109
+
110
+ Off by default. With `"voice": { "enabled": true, "language": "fr" }`, each `say` is spoken and the video waits for each sentence to finish. Without voice, `say` still shows as a caption. Getting the voice:
111
+
112
+ 1. If `CARTESIA_API_KEY` or `ELEVENLABS_API_KEY` is set in the environment, it is used (Cartesia first; `"provider": "elevenlabs"` to choose).
113
+ 2. If neither is set, offer the person the choice:
114
+ - give a key (set `CARTESIA_API_KEY` from https://play.cartesia.ai/keys or `ELEVENLABS_API_KEY` from https://elevenlabs.io/app/settings/api-keys, then restart the agent); or
115
+ - make the voice themselves: write the narration as a ready-to-paste script, one numbered line per spoken step, with the language and the tone, and ask them to generate it in ElevenLabs' or Cartesia's web app and save one file per line as `voice/01.mp3`, `voice/02.mp3`… next to the scenario. Then give each spoken step its `"audio": "voice/NN.mp3"` and run `render` (no new filming).
116
+ - or go without voice: captions only.
109
117
 
110
118
  ## Direction that makes a good video
111
119
 
112
- - **Start on a title card** that names the feature, and **end on a card** with the result or call to action.
113
120
  - **One action per step**, and a `say` on the steps that matter, not on every one. Narration: short spoken sentences, present tense, what the viewer gains ("One click, and the conversation is yours"), not what the cursor does ("Now I click the button").
114
121
  - **Show before you act** when the control is hard to spot: a `callout` on the button, then the `click`.
115
122
  - **End on the proof**: a `highlight` or `hold` on the result (the saved item, the green status, the sent message).
116
123
  - **Let zooms work**: they follow clicks and typing automatically. Use `"zoom": false` on a step whose result appears far from the click (a toast, a side panel), or `zoomTo` to frame the result.
117
124
  - **Realistic data and typing**: real-looking names and messages, 3 to 10 words typed, not paragraphs.
118
125
  - **Keep it short**: under a minute; cut steps that don't move the story.
119
- - **Logins**: run `screenplay login <url>` once (a person signs in, the session is saved to `.screenplay/auth.json`) and set `auth.storageState`; or log in with steps using `${EMAIL}` / `${PASSWORD}` and `secret: true`.
126
+ - **Logins**: run `npx agent-screenplay login <url>` once (a person signs in in the window that opens, then closes it; the session is saved to `.screenplay/auth.json`) and set `auth.storageState`; or log in with steps using `${EMAIL}` / `${PASSWORD}` and `secret: true`.
120
127
 
121
128
  ## When something goes wrong
122
129
 
123
- - `check` fails on a target → use one from the listed visible targets, or add a `wait` for it.
130
+ - `check` fails on a target → use one from the listed visible targets, or add a `wait` for it. A listed `css=… (shows "…")` is a button whose text is drawn by CSS (some cookie walls): copy it as is.
131
+ - "shows a bot check that needs a person" (Cloudflare) → run `npx agent-screenplay login <url>` (in the background: it waits), and tell the person: pass the check in the browser window that opened, accept the cookies if asked, then **close the window**. Closing saves the session; do not ask them to press Enter in a terminal. Then set `"auth": { "storageState": ".screenplay/auth.json" }` and check again.
124
132
  - "covered by …" → something (a toast, a cookie banner, a modal) sits on the element: close it first with a step.
125
133
  - "the typed text did not appear" → the field was not focused: use `into` to click it.
126
134
  - A wait is long (page loads, AI answers) → keep it: long waits are sped up in the video automatically.