Skip to main content

The Show

A framework for running agents unattended — scenes, fallbacks, adaptive variations, urgent contact escalation, and a programme at the end.

Status: v1.1.0 — production-ready, on PyPI, used in real operations at Short+Sweet International. Open source, runtime free.


What it is

The Show is an orchestration runtime for creative work. It uses a theatrical metaphor — programmes, scenes, Stage Manager, Urgent Contact — because the metaphor maps onto how creative operators already think. You write a programme. The Stage Manager runs it, persisting state after every scene. When something needs a human decision, the Urgent Contact system sends a signed, authenticated request across Telegram, email, SMS or WhatsApp. You respond. The show continues.

The specific differentiator: async cancellation mid-LLM-call across four signed-link channels with typed response schemas and per-channel authentication. When the first valid response comes in, outstanding sends are cancelled. That combination doesn't exist elsewhere as a coherent system.

The v1.0 release was built by running The Show on itself — examples/build-v1.0-release.yaml is the programme that built v1.0. It's in the repo.


Here's what a programme looks like — three scenes from the Curiosity Cat launch announcement that ran on 23 April 2026:

running-order:

  - scene: draft_english
    title: "Draft English announcement"
    outputs:
      post: {type: object}
    principal:
      method: sub-agent
      agent: gemini
      brief: "Draft a 280-char social post for the Curiosity Cat launch."
      params: {model: gemini-flash}
    cut: {condition: escalate, reason: "Cannot proceed without English draft"}

  - scene: draft_arabic
    title: "Draft Arabic announcement"
    depends-on: [draft_english]
    inputs:
      english_post: from(draft_english.post)
    outputs:
      post: {type: object}
    principal:
      method: sub-agent
      agent: gemini
      brief: "Translate the English post to natural Arabic for the UAE market."
      params: {model: gemini-flash}
    cut: {condition: escalate}

  - scene: approve_drafts
    title: "Operator approval"
    depends-on: [draft_english, draft_arabic]
    outputs:
      decision: {type: string}
    principal:
      method: human-approval
      brief: "Reply APPROVE to publish, REJECT to abort."
    cut: {condition: escalate}

The full programme is at examples/curiosity-cat-launch-announcement.yaml. The operator guide explains the metaphor and the mechanics in detail — see docs/OPERATOR_GUIDE.md.


Current state

Version: v1.0.0 — 231 passing tests

What works

  • SQLite state (~/.the-show/state/<show-id>.db, WAL mode)
  • Crash recovery — interrupted shows prompt for resume on next run
  • Full scene state vocabulary including cascading-dependency-failure and played-fallback-N
  • Per-strategy success-when overrides (falls back to scene-level)
  • Basic schema validation in meets_success (list / dict / string / number)
  • Markdown-fence sanitisation on untrusted output (sanitise.py)
  • Field-validator hook — logs INFO, skips (real validators are a later session)
  • Idempotency key generation for side-effectful strategies (logged to event DB)
  • Urgent Contact — real dispatcher replacing the Session 2 stub:
    • Three auth methods: channel-native, reply-token (6-digit), signed-link (HMAC-SHA256)
    • Strict response parsing: APPROVE / REJECT / STOP / CONTINUE only; invalid format sends a correction prompt and keeps polling
    • Sequential and parallel dispatch modes; critical severity forces parallel
    • Cancellation of pending sends on first valid response
    • Exhaustion path (blocked-no-response) when all contacts fail to reply
    • DAG pruning: exhausted/blocked scene cascades cascading-dependency-failure to all transitive dependents
    • Frequency throttle: default 3 unplanned matters per show; human-approval scenes always exempt; critical always bypasses
    • Mock channel for testing (file-drop at ~/.the-show/urgent-mock/)
  • Real channel adapters (Session 4):
    • Telegram — dedicated urgent-contact bot, polling-based, channel-native auth
    • Email — SMTP send + signed action links, HMAC-SHA256 tokens, 24h expiry
    • WhatsApp — skeleton with setup checklist; send() raises NotImplementedError until Meta onboarding complete
    • SMS — Twilio, reply-token auth
    • Signed-link server — Flask app on port 5099; handles email link clicks + WhatsApp / Twilio webhooks
  • load_adapters() in dispatcher.py — registers only configured adapters; warns if credentials missing; mock always available
  • Programme reads from SQLite event log
  • tests/ — 146 passing pytest tests

Known stubs (addressed in later sessions)

  • WhatsApp send() raises NotImplementedError until Mark completes Meta Business API onboarding
  • Link server is localhost-only — needs reverse proxy or Cloudflare Tunnel for WhatsApp/Twilio webhooks
  • Execution Monitor — not running (Session 5)
  • STOP keyword — parsed and returned but does not yet abort the whole show (Session 5)
  • Field-level validators — hook exists, no real validators (later)
  • Basic schema validation only — no JSON Schema deep-validation (later)

Install

# From PyPI
pip install the-show

# Or from source
git clone https://github.com/markscleary/the-show.git
cd the-show
pip install -e ".[dev]"

See docs/QUICKSTART.md for a five-minute walkthrough.


How to run

# Validate a show file
the-show validate example_show.yaml

# Run a show (with crash-resume)
the-show run example_show.yaml

# Inspect current state
the-show peek outreach-enrichment-001

# Regenerate programme from saved state
the-show programme outreach-enrichment-001

# Print event log
the-show events outreach-enrichment-001 [--since=<ISO>] [--limit=N]

# Run tests (from a source checkout)
pytest tests/

State DB: ~/.the-show/state/<show-id>.db Programme output: ~/.the-show/state/<show-id>/programme.md and programme.json

Testing Urgent Contact end-to-end

Run a show that contains a human-approval scene. When the dispatcher raises a matter via the mock channel, drop a JSON response file to resolve it:

# 1. Start the show — it will block on the human-approval scene
uv run python cli.py run example_show.yaml

# 2. In another terminal, find the matter ID from the event log
uv run python cli.py events outreach-enrichment-001 --limit=5

# 3. Drop a response for that matter ID (replace <matter-id> with the real value)
mkdir -p ~/.the-show/mock_responses
echo '{"keyword": "APPROVE", "auth_token": null}' \
  > ~/.the-show/mock_responses/<matter-id>.json

# 4. The dispatcher will pick it up within the next poll interval (default 5 s)
#    and unblock the show.

The THE_SHOW_POLL_INTERVAL environment variable controls the poll delay (seconds, default 5). Set it to 0 or 0.01 in tests for instant resolution.


Files

  • models.py — core dataclasses (Strategy now has success_when)
  • loader.py — YAML loading and validation
  • executor.py — scene execution loop (resume, all v0.4.1 states, real urgent contact)
  • state.py — SQLite state layer (WAL, resume, event log, urgent matter / send tables)
  • sanitise.py — markdown fence stripper for untrusted output
  • programme.py — reads from SQLite, generates markdown + JSON
  • adapters.py — stub adapters + idempotency key utilities
  • cli.py — CLI: validate / run / peek / programme / events
  • example_show.yaml — 5-scene example (covers all Session 2 + 3 features)
  • urgent_contact/ — Urgent Contact subsystem
    • dispatcher.py — raise matters, fire sends, poll, cancel, return resolution
    • auth.py — channel-native / reply-token / signed-link auth
    • parser.py — strict APPROVE / REJECT / STOP / CONTINUE keyword parser
    • throttle.py — per-show matter frequency limit
    • degradation.py — DAG pruning for cascading-dependency-failure
    • channels/base.py — ChannelAdapter protocol and InboundResponse type
    • channels/config.py — env var config helpers for all channels
    • channels/mock.py — file-drop mock channel for testing
    • channels/telegram.py — polling Telegram Bot API adapter
    • channels/email.py — SMTP send + signed-link poll adapter
    • channels/whatsapp.py — WhatsApp Business API skeleton
    • channels/sms.py — Twilio SMS adapter
    • link_queue.py — shared SQLite for email/SMS/WhatsApp webhook responses
    • link_server.py — Flask server: /respond, /whatsapp-webhook, /twilio-webhook
  • tests/ — pytest suite (146 tests)

Channel configuration

Copy .env.example to .env and fill in credentials:

cp .env.example .env
# edit .env

Channels activate automatically when their env vars are set. Missing vars emit a warning and the channel is skipped; the mock channel is always available.

Telegram — fastest to activate: create a bot with BotFather and set URGENT_TELEGRAM_BOT_TOKEN plus the authorised user IDs.

Email — create a Gmail App Password, set URGENT_SMTP_* vars and URGENT_EMAIL_SIGNING_SECRET.

WhatsApp — requires Meta Business API onboarding (1–7 days). See channels/whatsapp.py class docstring for the setup checklist.

SMS — Twilio account required. Trial accounts can only send to verified numbers.

Running the link server

The link server receives email link clicks and Twilio/WhatsApp webhooks.

# Manual (for development)
.venv/bin/python -m urgent_contact.link_server

# Via launchd (production — after filling in the plist env vars)
launchctl load ~/Library/LaunchAgents/org.shortandsweet.urgent-link-server.plist

For WhatsApp and Twilio webhooks (which require HTTPS), use ngrok:

ngrok http 5099
# Use the resulting https://*.ngrok.io URL in Meta / Twilio dashboards

Documentation

  • Quickstart — get a programme running in 5 minutes.
  • Operator Guide — the theatrical metaphor, how scenes work, approval gates, urgent contact mechanics. The starting point for anyone running The Show.
  • Spec v0.4.2 — formal specification and patch notes.

Coming in Session 5

  • Execution Monitor — watches running scenes, triggers Urgent Contact on anomalies

Coming in Session 6

  • First real show end-to-end with live Telegram notifications

Release files for the-show 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for the-show 1.1.0
File Size Uploaded
the_show-1.1.0.tar.gz 117.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for the-show 1.1.0
File Interpreter ABI Platform
the_show-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 180.0 kB

Release files / the_show-1.1.0.tar.gz

Download URL the_show-1.1.0.tar.gz
Size 117.1 kB
Tags Source
SHA-256 checksum
How to use checksums
b2f2b66de2f4ade56d34a6b402e33113470013e94f00721be77b8c02fc37116a
BLAKE2b-256 checksum
How to use checksums
b3ee87f5292ee79239a536c88114c7352636ddceeed6b4bb7d9cd27144f1bef6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.15

Release files / the_show-1.1.0-py3-none-any.whl

Download URL the_show-1.1.0-py3-none-any.whl
Size 62.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b1c948f5a79419b35041f422e177d698ebe8f1658d8c2ac634df248395dfb8df
BLAKE2b-256 checksum
How to use checksums
9ff3cbbc9e75d550a897ae66c2ef25515caedba8953537ae128921784eb5b117
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.15

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page