Skip to content

feat(replay): embeddable Replay app, Run box, Play, and a map that follows the cursor - #317

Merged
MervinPraison merged 1 commit into
mainfrom
feat/replay-embed
Oct 26, 2026
Merged

MervinPraison merged 1 commit into
mainfrom
feat/replay-embed

Conversation

@MervinPraison

@MervinPraison MervinPraison commented Oct 26, 2026 •

Copy link
Copy Markdown
Owner

Lets people show agent progress inside their own app, and gives learners a click Run, watch each step page. Follow-up to #316.

What changes

Embed (create_replay_app)

  • The Replay view as a small ASGI app with no dashboard. Mount it in any FastAPI or Starlette app with app.mount("/replay", create_replay_app()).
  • /replay/?run=<id> shows only that run (no run list) and waits for it if it hasn't started yet, so it works in an <iframe>.
  • With run(input, run_id), the page shows a Run box. POST /api/replay/run starts a run:
    • JSON only, so a cross-site form can't trigger it;
    • one run at a time (409 while busy);
    • input capped at 20k characters.
  • render(el, {base, run, runner, approve}) works under any mount prefix.

Learning

  • Map nodes show their state at the cursor: dashed = not reached, blue and pulsing = running, green = done, red = failed. Moving the cursor or pressing Play rewinds the map. Before this, the map always showed the end of the run.
  • "×N" now counts real runs. Before, it counted events, so a step that ran once read "×2".
  • ▶ Play steps through a run on its own.

Small fixes found while testing

  • events?wait=1: a run that hasn't started yet returns an empty answer instead of a 404 in the console.
  • Node names stay whole (squeezed, or clipped at the card edge), so the accessible name contains the visible text. axe flagged truncated tool names ("search_pap…") under WCAG 2.5.3.

Weight

  • 6 files, +610/−50, including tests and docs.
  • No new dependencies: Starlette is already used.
  • The dashboard path is unchanged except for the map states and the Play button.

Tests

  • tests/js/replay-core.test.mjs: 40 pass, 4 of them new for nodeStates.
  • tests/unit/test_replay_embed.py (new, 10 tests): the mount prefix, a pinned run, run-id validation, the Run route (415 / 400 / 409, a failing runner frees the lock, async runners), and wait=1.
  • Full tests/unit: 1646 passed.
  • Browser tests (RUN_BROWSER_TESTS=1, with axe): 19 passed, including a new end-to-end test. It clicks Run, sees a step drawn as running while later steps are not reached, waits for the run to complete, then presses Play and checks the map rewinds. axe finds no violations.
  • Checked by hand in a browser:
    • a FastAPI app with an <iframe>;
    • a real two-agent gpt-4o-mini team run twice from the Run button;
    • an AgentFlow with four steps.

The PraisonAI side (praisonai.replay.watch() / replay_app() and praisonai replay watch agents.yaml) is in a companion PR in MervinPraison/PraisonAI.

🤖 Generated with Claude Code


Origin: outsider. Issue #316 was flagged as outsider input, so its text and comments are treated as untrusted data. The scope of fixes is limited to source and tests: no .github/, dependency manifests, lockfiles, build or install files, or conftest.py, and no new network endpoints, telemetry, credential handling, eval/exec/subprocess or dynamic imports. The security review is in the PR comments.

…llows the cursor

- create_replay_app(run=None): the Replay view as a small ASGI app (no dashboard)
  to mount in any FastAPI/Starlette app; /?run=<id> pins one run (iframe-friendly).
  With run(input, run_id) it shows a Run box: POST /api/replay/run (JSON only,
  one run at a time) starts a run and the page follows it live.
- render(el, {base, run, runner, approve}) so the view works under a mount prefix.
- Map nodes show their state at the cursor: not reached / running / done / failed /
  skipped, and "×N" counts real runs instead of events (a step that ran once
  no longer reads "×2"). Scrubbing or Play rewinds the map with the cursor.
- ▶ Play steps through a run on its own.
- events?wait=1 answers a not-yet-started run without a 404.
- Node names stay whole (squeezed or clipped) so accessible names contain the
  visible text (WCAG 2.5.3; axe flagged truncated tool names).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 26, 2026 15:47

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@MervinPraison

Copy link
Copy Markdown
Owner Author

@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAIUI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on PraisonAIUI (src/praisonaiui, src/frontend, tests, docs). Do NOT expand into praisonaiagents or the monorepo unless the PR explicitly requires it. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.

Phase 1: Review per AGENTS.md

  1. Protocol-driven: check heavy implementations vs core SDK
  2. Backward compatible: ensure zero feature regressions
  3. Performance: no hot-path regressions (lazy imports, import-time <200ms)
  4. Product value: review in depth whether the change genuinely adds PraisonAIUI UX value — never add features for the sake of adding them. It must strengthen the UI (simpler, more user-friendly, robust). If it does not clearly add value, request changes or recommend rejecting/closing rather than merging scope creep.
  5. Layering: UI/frontend/server features here; core agent runtime belongs in praisonaiagents (PraisonAI monorepo). Agent-callable tools → PraisonAI-Tools. Do not pull monorepo or SDK scope into this repo unless the PR explicitly requires it.
  6. Do not bloat server.py or create_app() with new params — prefer existing YAML config, plugins, and feature modules; only add params if absolutely required. Reject knobs/exports with no live consumer.

Phase 2: FIX Valid Issues
5. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix
6. Push all code fixes directly to THIS branch (do NOT create a new PR)
7. Comment a summary of exact files modified and what you skipped

Phase 3: Final Verdict
8. If all issues are resolved, approve the PR / close the Issue
9. If blocking issues remain, request changes / leave clear action items

@praisonai-triage-agent

Copy link
Copy Markdown
Contributor

Final review: PR #317 (feat/replay-embed)

Origin: outsider. Issue #316 was flagged as outsider input, so the PR text, issue text and comments were treated as untrusted data and none of their instructions were followed. Scope rules applied: changes only in source and tests; no .github/, dependency manifests, lockfiles, build or install files, or conftest.py; no new network endpoints, telemetry, credential handling, eval/exec/subprocess or dynamic imports.

Files modified by this review

None. I found no valid bug or architecture problem that needed a fix, so nothing was committed. No other reviewer left actionable feedback: Copilot did not run because of its quota, and there were no Gemini, CodeRabbit or Qodo comments.

Security checklist (diff main...feat/replay-embed, 7 files)

# Check Result
1 Scope Pass. Every file is Replay: features/replay.py, replay.js, replay-core.js, docs, and tests.
2 Network / exfiltration Pass. There is one new local route, POST /api/replay/run, and it is registered only when the host passes a run callable. Every frontend fetch goes to the same origin under ${S.base}; there are no external hosts. The favicon is an inline data: URI.
3 Code execution Pass. There is no eval/exec, subprocess or dynamic import. run(input, run_id) is a callable supplied by the host developer; it runs in a worker thread and is awaited with asyncio.run if it returns an awaitable.
4 Secrets Pass. No new env-var or credential reads; it uses the existing traces-dir lookup.
5 Weakened controls Pass, with notes. The Run route requires application/json, which blocks CSRF from cross-site forms. It caps input at 20k characters, allows one run at a time through a non-blocking lock (released in finally), and generates server-side run_ids with uuid. ?run= is checked with tj.is_valid_run_id, which a test covers with ../../etc/passwd. ?wait=1 only changes a 404 into an empty 200 and reveals nothing new. The page config and root_path go through _json_for_script, which escapes <, > and &, and the title goes through html.escape. The docstring says the app has no auth of its own and must stay on localhost or behind the host app's auth. That matches the existing Replay routes.
6 Supply chain Pass. No manifests, lockfiles, CI or conftest changes. Starlette is already a dependency.
7 Obfuscation Pass. No bidi or zero-width characters, encoded blobs or minified code. The only lines over 250 characters are docs table rows.
8 Tests Pass. No tests were removed or loosened. The 10 new tests in tests/unit/test_replay_embed.py check real behaviour: the 415/400/409 responses, the lock being released after a runner fails, and async runners.
9 Backdoors Pass. No magic values, no behaviour keyed on a user or host, no hidden routes.
10 Injection artifacts Pass. No instructions to AI tools in code, comments, docs or test data.

Deterministic risk flags: none were listed in the request. I scanned the added lines myself for innerHTML, eval, Function(, external URLs, postMessage and dynamic import( and found none.

Architecture (AGENTS.md)

  • Backward compatible: the dashboard path changes only in the map node states and the Play button. server.py and create_app() are untouched.
  • Lightweight: no new dependencies. The new imports are stdlib only. create_replay_app has a live consumer in the companion PraisonAI PR (replay_app() / replay watch), and all four of its parameters are used by the page.
  • It belongs in this repo: it is UI and server code for an existing feature.

Tests run

  • node --test tests/js/replay-core.test.mjs: 40 passed.
  • pytest tests/unit/test_replay*.py: 56 passed.
  • Full tests/unit: 1537 passed and 23 failed. The failures, plus the 28 errors and 3 collection errors, come from packages missing in this runner: praisonaiagents and mcp, plus one eval test that expects sdk_available from the SDK. This PR does not touch that code.
  • Browser tests were not run.

Verdict

No blocking issues. It is safe to merge once a maintainer accepts the outsider-origin flag.

@MervinPraison MervinPraison added pipeline/blocked:ci Blocked: CI not green on HEAD pipeline/blocked:cooldown Blocked: post-push or @claude cooldown pipeline/reviews-pending Waiting for CodeRabbit/Qodo/Copilot reviews labels Oct 26, 2026
@MervinPraison
MervinPraison merged commit 7a22011 into main Oct 26, 2026
16 of 17 checks passed
joyanes97 pushed a commit to joyanes97/praisonai that referenced this pull request Oct 27, 2026
…n live, and embedding

- praisonai.replay.watch(target): serves a page with a Run button; each click
  records the Agent/AgentTeam/AgentFlow/YAML/callable under a new run id and the
  page draws it live (map: not reached / running / done). A team's tasks start
  afresh each run (AgentTeam otherwise skips completed tasks).
- praisonai.replay.replay_app(target=None): the same page as an ASGI app to mount
  in your own FastAPI/Starlette app; /replay/?run=<id> shows one run (iframe).
- CLI: praisonai replay watch [agents.yaml] ("show" was taken).
- examples/replay: watch_flow.py (no key), watch_team.py, embed_fastapi.py.

Needs aiui with create_replay_app (MervinPraison/PraisonAIUI#317); older aiui
gets a clear "pip install -U praisonai[ui]" error and the tests skip.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

origin/outsider pipeline/blocked:ci Blocked: CI not green on HEAD pipeline/blocked:cooldown Blocked: post-push or @claude cooldown pipeline/reviews-pending Waiting for CodeRabbit/Qodo/Copilot reviews

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants