feat(demo): Stage 2 PR-2/3/4 aggregate — land remaining demo work on master#229
Merged
Conversation
The Terminal in demo mode now plays back a recorded PTY transcript using
xterm.js, replacing the static "terminal disabled" stub for workspaces
that have a registered transcript fixture. Stage 1's stub remains the
fallback for any workspace without one.
Transcript format (`ui/src/demo/types.ts`): `{label, durationMs, frames:
[{atMs, bytesB64}]}`. bytesB64 is base64-encoded raw PTY output bytes;
ANSI escapes, colors, cursor moves all replay faithfully because they
ride along inside the bytes.
DemoTerminalReplay (`ui/src/demo/DemoTerminalReplay.tsx`) — looks up a
transcript by workspace id, instantiates xterm with the same theme +
addons as the real Terminal, polls the container until it has real
dimensions before fitting + writing (avoids xterm's "Cannot read
properties of undefined (reading 'dimensions')" race in
Viewport.syncScrollArea), then schedules `term.write` calls at the
recorded timestamps via rAF. End of transcript → "↻ Replay again"
button.
Recorder (`ui/src/demo/recorder/recorder.ts`) — dev-mode helper that
monkey-patches `window.WebSocket` to tap server→client frames on URLs
matching `/api/workspaces/pty`, captures `{atMs, bytesB64}` per frame,
and on `__demoRecord.stop(label)` packages them as a JSON download.
Main.tsx auto-loads it in dev mode (NOT demo mode, NOT prod) so the
console-driven recorder is available alongside the real backend.
Placeholder transcript (`fixtures/transcripts/welcome.ts`) — hand-
crafted Claude-Code-style intro with banner, typed query, tool calls,
and a multi-paragraph response describing OpenAlice's architecture.
PR-3 will replace this with a real recorded session captured via the
recorder tool.
Verified:
- pnpm -F open-alice-ui exec tsc -b clean
- pnpm -F open-alice-ui build:demo: 277KB demo chunk + 2.83MB main chunk
- pnpm -F open-alice-ui build (prod): zero __demoRecord/
DemoTerminalReplay/transcriptsByWorkspace/welcomeTranscript/
startRecording/stopRecording/VITE_DEMO_MODE refs in dist
- pnpm dev:demo + Playwright walk: transcript plays back end-to-end,
ANSI colors + bold + dim render correctly, line wrapping respects
container width, "Replay again" button re-runs from start
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nscript) Wires three demo surfaces into a coherent story for the visitor: 1. Inbox: shows a single agent-written report from a workspace named "demo" timestamped 5 minutes ago — "AAPL Q1 — Hidden Deceleration Signal" with a fully-rendered markdown document (TL;DR + analysis table + recommendations) inline. 2. Workspace file handler: returns the markdown content for `research-AAPL-q1.md` from `demoWorkspaceFiles` map. Inbox's readWorkspaceFile() call pulls it live (no snapshot), matching real product behavior. 3. PTY replay (welcome.ts rewritten as `aaplResearchTranscript`): shows the session that produced the report — user asks "what jumped out from Apple's Q1 earnings?", agent reads filings, computes services-rev YoY across quarters, finds the three-quarter deceleration, writes the report, inbox_pushes. Cross-link is implicit and clean: the Inbox entry has `workspaceId: DEMO_WORKSPACE_ID`. Clicking opens the workspace, the session terminal plays the transcript that produced the entry. No explicit "this entry came from that session" pointer needed — the narrative reads itself. Also scripts the SSE event timeline (handlers/events.ts + fixtures/events.ts): 6 historical events spanning ~5 minutes covering agent.work.started → tool_calls → write_file → inbox.entry_created → agent.work.completed. Emitted as one burst on stream connect to populate the events feed. Tree-shaking fix (the necessary side-effect of this PR): Terminal.tsx now lazy-imports DemoTerminalReplay via React.lazy. With the prior static import, Rollup conservatively kept the transcript file in the prod main bundle because its top-level frame-building IIFE has module- scope side effects. Lazy import code-splits the demo terminal into its own 5.7KB chunk that is never fetched in prod (gated by import.meta.env.VITE_DEMO_MODE at the call site). Verified: - pnpm -F open-alice-ui exec tsc -b clean - pnpm -F open-alice-ui build (prod): main chunk has zero transcript / fixture / handler strings; only "DemoTerminalReplay" literal (the lazy-import module specifier) remains. Separate DemoTerminalReplay-*.js chunk (5.7KB / 2.5KB gz) exists but is never fetched in prod. - pnpm -F open-alice-ui build:demo: 279KB demo chunk + 2.82MB main, builds clean. - pnpm dev:demo + Playwright: Inbox shows demo entry, markdown report renders inline, navigating to workspace plays the AAPL research transcript end-to-end. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fills out the long tail of fixture content so the demo doesn't look gutted once a visitor scrolls past the Inbox/workspace flagship flow from PR-3. **3 trading accounts (instead of 1)** — - Alpaca Paper: \$52,840 NL, 4 stock positions (AAPL/NVDA/GOOG/AMD) with realistic avg-cost vs market-price spreads - IBKR Demo: \$247,310 NL, mix of ETFs (SPY, QQQ, TLT) + an underwater AAPL options position to show non-trivial P&L - Binance Spot: \$15,032 NL (USDT base), BTC and ETH spot positions Equity curve generator uses a seeded random walk per UTA (30 daily points, distinct vol + drift per account) so the Portfolio chart shows plausible-looking lines without the numbers shifting on each page reload — visitors who refresh see consistent state. **6 news articles** — Reuters/Bloomberg/CNBC/WSJ/CoinDesk spread across the last 22 hours covering AAPL Q1, NVDA capex, Fed minutes, BTC, SPY, TLT. The AAPL article (timestamped 30m ago) ties to the Inbox AAPL research from PR-3 (timestamped 5m ago) — "news landed → agent dug in → wrote up the deceleration signal". **4 cron jobs** — Morning market prep (weekdays 8:30 AM), EOD snapshot + journal (weekdays 4:05 PM), Weekly position review (Fridays 5 PM, disabled), AAPL watch alert (every 15m). Mix of cron- schedule vs every-N-minutes formats; mix of enabled vs disabled. Skipped from this PR: topology graph fake (Dev page, low marketing value), populated agent-status log (the events SSE in PR-3 already carries the "system is alive" signal). Verified: - pnpm -F open-alice-ui exec tsc -b clean - pnpm -F open-alice-ui build (prod): zero fixture name leaks in main bundle (`Alpaca Paper`, `Binance Spot`, `Morning market prep`, `demoCronJobs`, `demoNewsArticles` — all absent) - pnpm dev:demo + Playwright: portfolio page renders chart with the random-walk curve + cumulative equity numbers, news page lists all 6 articles with badges, sidebar shows 3 trading accounts Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lands the three "stuck" stacked PRs from Stage 2 — #222 (PTY replay), #223 (flagship scenario), #224 (fixture richness) — directly on master. These were all merged into their stacked base branches but never bumped to master; this PR is the aggregate.
Methodology: three clean cherry-picks from the original branches onto current master tip. No conflicts, no MiniMax/auth work disturbed (the parallel PR #228 changes touch different file regions than the demo work).
Why this is needed
The Stage 2 stack used `base: feat/ui-demo-stage2-skeleton` for PR-2, `base: feat/ui-demo-stage2-pty-replay` for PR-3, `base: feat/ui-demo-stage2-scenario` for PR-4. When "Merge PR" was clicked on all four, each landed in its declared base — so only PR-1 (#221), which had `base: master`, actually shipped to master. PR-2/3/4 are sitting on intermediate branches that nothing else consumes.
Rather than force-push to rewrite history on those merged branches, this PR cherry-picks the three feat commits onto a fresh branch off master.
Contents
Three cherry-picked commits (preserve original commit messages):
Diff scope
21 files, +1016 / -77, all under `ui/src/demo/` plus the existing `ui/src/main.tsx` (recorder dev-import) and `ui/src/components/workspace/Terminal.tsx` (lazy-import the demo terminal so prod bundle stays clean).
Verification
Cleanup
Once this lands, the three stale stacked branches (`feat/ui-demo-stage2-skeleton`, `feat/ui-demo-stage2-pty-replay`, `feat/ui-demo-stage2-scenario`) can be deleted manually if you want — they're dead refs now. `feat/ui-demo-stage2-fixtures` can also go. None of them are GitHub-protected.
Boundary touch
None — no trading / auth / broker / migrations code modified.
🤖 Generated with Claude Code