Keeping the demo video fresh#

The demo video is generated by a scripted Playwright runner that drives a real Telegram Web session against a fake ACP agent. Everything — timing, messages, agent responses — is declared in a single JSON file. To update the video you mostly only touch that file.

How it works#

demo_story.json          ← the only file you normally edit
      │
      ▼
telegram_web_demo.py     ← Playwright driver (opens browser, types, clicks)
      │
      ▼
run_demo_bot.py          ← Telegram bot + fake ACP agent wired together
      │
      ▼
fake_acp_agent.py        ← replays agent_routes from demo_story.json

telegram_web_demo.py reads the story, opens a persistent Chromium profile logged into Telegram Web, and executes each user_steps entry in order. After each step it waits for a wait_for_text pattern to appear before typing the next message — this keeps the pacing locked to actual bot responses rather than fixed sleeps.

The fake agent (fake_acp_agent.py) receives the bot’s ACP prompts and replays whichever agent_routes entry matches the prompt text. It emits tool events with configurable delays, then sends the final text/images/files.

Recording a new video#

One-time login#

uv run scripts/demo/telegram_web_demo.py --mode login

Scan the QR code that appears in the browser. The session is saved in .cache/telegram-web-profile/ and reused on every subsequent run.

Record#

cp .env.demo .env   # set TELEGRAM_BOT_TOKEN, TELEGRAM_ALLOWED_USER_IDS, etc.
uv run scripts/demo/telegram_web_demo.py

The recording is saved under artifacts/demo-videos/<timestamp>/. Pass --no-record to run without recording (useful for testing timing).

Changing the script#

Open scripts/demo/demo_story.json. The structure is:

runtime            — global typing speed and final pause duration
assets             — image/file payloads referenced by agent_routes
user_steps         — what the "user" types, in order
agent_routes       — what the fake agent replies to each prompt

Editing user messages#

Each entry in user_steps has:

field

meaning

text

the message that gets typed into Telegram

wait_for_text.pattern

regex that must appear on screen before typing

wait_for_text.timeout_seconds

how long to wait before giving up and continuing

wait_for_text.after_ms

extra pause after the pattern appears

actions

list of UI actions after sending: click_send_now or click_resume_choice

Editing agent responses#

Each entry in agent_routes has:

field

meaning

match_any

list of substrings; the first route where any substring matches the prompt wins

events

tool events to emit (kind, title, text, delay_ms)

final_text

plain-text message sent after all events

final_images

list of asset IDs to send as photos

final_files

list of asset IDs to send as file attachments

cancel_reply_text

reply sent if the agent is interrupted mid-route

Timing knobs#

  • delay_ms on each event — pause before that tool event is emitted.

  • wait_for_text.after_ms — pause on the user side after the pattern appears.

  • runtime.typing_delay_ms — per-character typing speed.

  • runtime.final_pause_seconds — how long the browser stays open after the last step (gives time for the final messages to settle before closing).

Increase wait_for_text.timeout_seconds if a step keeps timing out in CI or on slow machines; increase after_ms if the next message arrives before the previous reply has finished rendering.

Adding a new demo scenario#

  1. Add a new agent_routes entry with a unique id and match_any keywords.

  2. Add a user_steps entry whose text contains one of those keywords.

  3. Add a wait_for_text so the next step waits for the agent’s reply.

  4. If the route sends an image or file, add the asset to the assets map and put the asset file in scripts/demo/.

  5. Run with --no-record first to verify timing, then record.

File reference#

file

purpose

scripts/demo/demo_story.json

declarative script — edit this

scripts/demo/telegram_web_demo.py

Playwright driver

scripts/demo/fake_acp_agent.py

scripted ACP agent

scripts/demo/demo_scenario.py

typed dataclass parser for the JSON

scripts/demo/run_demo_bot.py

wires the bot + fake agent together

scripts/demo/demo.png

webcam photo asset

scripts/demo/release-diagnostics.pdf

PDF attachment asset

.cache/telegram-web-profile/

persistent Chromium login session (gitignored)

artifacts/demo-videos/

recorded .webm files (gitignored)