From c76939bb243448090d5dedb0ef7935f7937e9bf7 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Tue, 16 Jun 2026 12:38:04 +0000 Subject: [PATCH 1/3] feat: default semantic rendering to libghostty-vt --- README.md | 7 +- RELEASE.md | 6 +- design/ARCHITECTURE.md | 33 +-- docs/TROUBLESHOOTING.md | 10 +- docs/USAGE.md | 6 +- docs/prd/screen-hash/PRD.md | 4 +- .../README.md | 57 +++++ .../artifact-file-info.txt | 5 + .../artifact-manifest.json | 143 +++++++++++ .../artifact-sha256.txt | 5 + .../commands.sh | 223 ++++++++++++++++++ .../create.json | 12 + .../default-cast.json | 23 ++ .../default-screenshot.json | 20 ++ .../default-snapshot-artifact.json | 16 ++ .../default-snapshot.json | 114 +++++++++ .../default-wait.json | 14 ++ .../default-webm.json | 24 ++ .../doctor.json | 142 +++++++++++ .../environment.txt | 207 ++++++++++++++++ .../expected-renderer.json | 3 + ...xplicit-ghostty-web-snapshot-artifact.json | 16 ++ .../explicit-ghostty-web-snapshot.json | 16 ++ .../explicit-libghostty-vt-screenshot.json | 20 ++ .../explicit-libghostty-vt-webm.json | 24 ++ .../inspect.json | 50 ++++ .../recordings/default.cast | 11 + .../screenshots/default-screenshot.png | Bin 0 -> 13319 bytes .../explicit-libghostty-vt-screenshot.png | Bin 0 -> 13319 bytes .../version.json | 41 ++++ .../videos/default-webm.webm | Bin 0 -> 49225 bytes .../videos/explicit-libghostty-vt-webm.webm | Bin 0 -> 49695 bytes dogfood/CATALOG.md | 29 +-- src/cli/commands/inspect.ts | 2 +- src/cli/commands/record-export.ts | 2 +- src/cli/commands/screenshot.ts | 8 +- src/cli/commands/version.ts | 3 +- src/cli/context.ts | 86 ++++++- src/cli/main.ts | 5 +- src/host/hostMain.ts | 4 + src/host/renderer.ts | 4 + src/protocol/messages.ts | 1 + src/renderer/capabilities.ts | 177 +++++++++++++- src/renderer/index.ts | 2 + src/renderer/libghosttyVt/backend.ts | 10 +- src/renderer/names.ts | 4 + test/e2e/hello-prompt.test.ts | 6 +- test/integration/backend-selection.test.ts | 37 +++ test/integration/cli.test.ts | 5 +- test/integration/host-renderer-rpc.test.ts | 14 +- test/integration/lifecycle.test.ts | 8 +- test/unit/cli/context.test.ts | 77 +++++- test/unit/commands/create.test.ts | 1 + test/unit/commands/doctor.test.ts | 14 +- test/unit/commands/gc.test.ts | 1 + test/unit/commands/inspect.test.ts | 6 +- test/unit/commands/mark.test.ts | 1 + test/unit/commands/paste.test.ts | 1 + test/unit/commands/record-export.test.ts | 3 +- test/unit/commands/resize.test.ts | 1 + test/unit/commands/run.test.ts | 1 + test/unit/commands/screenshot.test.ts | 3 +- test/unit/commands/send-keys.test.ts | 1 + test/unit/commands/signal.test.ts | 1 + test/unit/commands/snapshot.test.ts | 1 + test/unit/commands/type.test.ts | 1 + test/unit/commands/version.test.ts | 4 +- test/unit/commands/wait.test.ts | 1 + test/unit/renderer/capabilities.test.ts | 64 ++++- .../unit/renderer/libghosttyVtBackend.test.ts | 1 + test/unit/renderer/registry.test.ts | 6 +- 71 files changed, 1744 insertions(+), 104 deletions(-) create mode 100644 dogfood/20260616-default-semantic-renderer/README.md create mode 100644 dogfood/20260616-default-semantic-renderer/artifact-file-info.txt create mode 100644 dogfood/20260616-default-semantic-renderer/artifact-manifest.json create mode 100644 dogfood/20260616-default-semantic-renderer/artifact-sha256.txt create mode 100755 dogfood/20260616-default-semantic-renderer/commands.sh create mode 100644 dogfood/20260616-default-semantic-renderer/create.json create mode 100644 dogfood/20260616-default-semantic-renderer/default-cast.json create mode 100644 dogfood/20260616-default-semantic-renderer/default-screenshot.json create mode 100644 dogfood/20260616-default-semantic-renderer/default-snapshot-artifact.json create mode 100644 dogfood/20260616-default-semantic-renderer/default-snapshot.json create mode 100644 dogfood/20260616-default-semantic-renderer/default-wait.json create mode 100644 dogfood/20260616-default-semantic-renderer/default-webm.json create mode 100644 dogfood/20260616-default-semantic-renderer/doctor.json create mode 100644 dogfood/20260616-default-semantic-renderer/environment.txt create mode 100644 dogfood/20260616-default-semantic-renderer/expected-renderer.json create mode 100644 dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot-artifact.json create mode 100644 dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot.json create mode 100644 dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-screenshot.json create mode 100644 dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-webm.json create mode 100644 dogfood/20260616-default-semantic-renderer/inspect.json create mode 100644 dogfood/20260616-default-semantic-renderer/recordings/default.cast create mode 100644 dogfood/20260616-default-semantic-renderer/screenshots/default-screenshot.png create mode 100644 dogfood/20260616-default-semantic-renderer/screenshots/explicit-libghostty-vt-screenshot.png create mode 100644 dogfood/20260616-default-semantic-renderer/version.json create mode 100644 dogfood/20260616-default-semantic-renderer/videos/default-webm.webm create mode 100644 dogfood/20260616-default-semantic-renderer/videos/explicit-libghostty-vt-webm.webm diff --git a/README.md b/README.md index fb26232b..f7291a41 100644 --- a/README.md +++ b/README.md @@ -100,10 +100,10 @@ Every session is backed by a real PTY (`node-pty`) and an append-only event log. Rendering uses Ghostty's terminal engine through two interchangeable backends (`--renderer`): -- **`libghostty-vt`** — Ghostty's native VT engine, bound into Node. Fast, browser-free semantic snapshots and `wait` checks. Also powers the dashboard. -- **`ghostty-web`** (default) — a headless web build of Ghostty driven by Playwright/Chromium. Adds pixel PNG screenshots and WebM video. +- **`libghostty-vt`** — Ghostty's native VT engine, bound into Node. It is the default for semantic snapshots, render-backed `wait` checks, and screen hashes when the optional native package is available. It also powers the dashboard. +- **`ghostty-web`** — a headless web build of Ghostty driven by Playwright/Chromium. It is the default for visual PNG screenshots and WebM video, and it remains the automatic semantic fallback when `libghostty-vt` is unavailable. -`ghostty-web` is a _reference_ renderer: it shows what a pinned Ghostty build draws, not a pixel-for-pixel guarantee of any particular native terminal window. That tradeoff is deliberate. The renderer sits behind an adapter, so native backends can be added later without changing the CLI contract. +`ghostty-web` is a _reference_ visual renderer: it shows what a pinned Ghostty build draws, not a pixel-for-pixel guarantee of any particular native terminal window. That tradeoff is deliberate. The renderer sits behind an adapter, so additional backends can be used without changing the CLI contract. Set `--renderer ghostty-web`, `AGENT_TTY_RENDERER=ghostty-web`, or `config.json` `defaultRenderer` to restore legacy all-`ghostty-web` behavior. ## Where it came from @@ -141,6 +141,7 @@ See [`docs/AGENT-SKILLS.md`](./docs/AGENT-SKILLS.md). `agent-tty` is `0.4.3` and focused on reliable, isolated, reviewable terminal and TUI automation through a stable CLI. - Linux and macOS are tier-1; Windows is tier-2 and not CI-tested. +- Semantic snapshots and render-backed waits prefer the optional `libghostty-vt` backend when it is available, then fall back to `ghostty-web`. - Screenshots and WebM video depend on Playwright/Chromium and the `ghostty-web` backend. - `run` is best for shell setup and command injection; it does not capture a child command's structured output or exit status. - Apache-2.0, runs entirely locally, no account or SaaS. diff --git a/RELEASE.md b/RELEASE.md index fa6bd23e..a7d900b1 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -9,7 +9,7 @@ For per-release changes, see [`CHANGELOG.md`](./CHANGELOG.md). For release mecha ## Supported capabilities - Reliable isolated session lifecycle management: `create`, `inspect`, `destroy`, and `gc` all work against isolated agent-tty homes. -- Renderer-backed screenshots, semantic snapshots, and WebM export for reviewer-visible proof artifacts. +- Renderer-backed screenshots, semantic snapshots, and WebM export for reviewer-visible proof artifacts; semantic operations prefer `libghostty-vt` when available, while visual artifacts use `ghostty-web`. - The `run` command for robust in-session command execution without having to simulate long shell setup scripts as manual keystrokes. - `doctor --json` with isolation-aware diagnostics for home resolution, renderer prerequisites, and screenshot viability. - An append-only event log that remains the canonical replay/export source of truth. @@ -17,7 +17,7 @@ For per-release changes, see [`CHANGELOG.md`](./CHANGELOG.md). For release mecha ## Explicitly out of scope -- Native renderer backends such as Ghostty native or kitty. +- Additional native renderer backends beyond the shipped `libghostty-vt` semantic renderer, such as kitty or platform terminal automation. - Mouse input support. - Remote or networked sessions. - An MCP wrapper. @@ -27,7 +27,7 @@ For per-release changes, see [`CHANGELOG.md`](./CHANGELOG.md). For release mecha ## Known limitations -- The renderer is the `ghostty-web` reference backend, not a native-terminal parity guarantee. +- Semantic operations may use `libghostty-vt`, but visual screenshots and WebM remain `ghostty-web` reference artifacts, not a native-terminal parity guarantee. - `run` completion detection relies on shell-visible echo of an injected boundary marker. - Screenshots and WebM export require Playwright/Chromium to be installed and discoverable. - The reviewed LazyVim workflow currently assumes Neovim `>= 0.11.2` plus its usual prerequisites; older Neovim builds are out of contract for that scenario. diff --git a/design/ARCHITECTURE.md b/design/ARCHITECTURE.md index 78aa892d..cb988c5b 100644 --- a/design/ARCHITECTURE.md +++ b/design/ARCHITECTURE.md @@ -21,7 +21,7 @@ This design intentionally describes a **general product**, not a Mux-specific im ## Current shipped status -The current `0.3.x` line is centered on reliable, isolated, reviewable terminal and TUI automation. The shipped surface includes `run` for robust in-session command execution, renderer/browser-path handling that respects isolated-home workflows, and isolation-aware `doctor --json` diagnostics on top of lifecycle, snapshot, screenshot, and export work. Larger asks such as native renderers, mouse input, remote/network sessions, MCP wrapping, and broader semantic TUI automation remain intentionally deferred. +The current `0.3.x` line is centered on reliable, isolated, reviewable terminal and TUI automation. The shipped surface includes `run` for robust in-session command execution, split semantic/visual renderer defaults, renderer/browser-path handling that respects isolated-home workflows, and isolation-aware `doctor --json` diagnostics on top of lifecycle, snapshot, screenshot, and export work. Larger asks such as additional native renderers, mouse input, remote/network sessions, MCP wrapping, and broader semantic TUI automation remain intentionally deferred. The repository now ships the first three milestones of this design plus Weeks 4–7 of CLI/artifact/lifecycle hardening, config/rendering/platform closeout, contract/introspection reconciliation, and Week 7 contract/doc ratification: @@ -53,10 +53,10 @@ The recommended v1 shape is: 3. **TypeScript/Node** implementation 4. **One session-host process per terminal session**, not a global daemon 5. **`node-pty`** for PTY/process control -6. **`ghostty-web`** as the default reference renderer -7. **Playwright** as the screenshot / replay harness +6. **`libghostty-vt`** as the preferred semantic renderer when available, with **`ghostty-web`** as the visual reference renderer and semantic fallback +7. **Playwright** as the screenshot / replay-video harness 8. **Event-log-as-truth** architecture so screenshots, snapshots, and recordings can be replayed deterministically -9. **Renderer adapter interface** from day one so native renderers can be added later without redesigning the CLI +9. **Renderer adapter interface** from day one so renderer defaults can evolve without redesigning the CLI ## Why this shape @@ -156,29 +156,30 @@ That lets v1: - render videos from replay, - and debug failures after the fact. -### 5) Reference renderer now, native renderers later +### 5) Semantic and visual renderers stay separated -V1 uses `ghostty-web` as a reference renderer for: +V1 uses two Ghostty-backed renderer paths by default: -- semantic snapshots, -- deterministic screenshots, -- deterministic video replay. +- `libghostty-vt` for semantic snapshots, screen hashes, and render-backed waits when the optional native package is available, +- `ghostty-web` for deterministic screenshots and deterministic video replay, +- `ghostty-web` again as the semantic fallback when native rendering is unavailable. -The architecture reserves native backends for later: +The architecture still reserves additional native backends for later: - WezTerm-like native automation, -- Ghostty native automation, +- platform-specific terminal automation, - platform-specific compatibility runs. ## Tiered truth model `agent-tty` should treat terminal truth as layered rather than singular. -| Layer | Source of truth | What it answers | -| ---------------------- | --------------------------- | --------------------------------------------------------- | -| Execution truth | PTY + event log | What bytes, signals, and resize events actually occurred? | -| Reference visual truth | `ghostty-web` replay/render | What does a pinned reference renderer show? | -| Native visual truth | Future native adapter | What does a real platform terminal show? | +| Layer | Source of truth | What it answers | +| ---------------------- | ---------------------------------- | --------------------------------------------------------- | +| Execution truth | PTY + event log | What bytes, signals, and resize events actually occurred? | +| Semantic renderer truth | `libghostty-vt` or fallback replay | What terminal cells/text does Ghostty's VT state expose? | +| Reference visual truth | `ghostty-web` replay/render | What does a pinned reference renderer show? | +| Native visual truth | Future native adapter | What does a real platform terminal show? | This prevents v1 from pretending reference rendering is identical to native platform rendering. diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index 11aa6ea0..01d64da1 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -35,13 +35,14 @@ Affected commands usually include: Check `doctor --json` for: +- `libghostty_vt_available` (preferred semantic renderer and dashboard) - `playwright_available` - `browser_cache_accessible` - `browser_launch` -- `ghostty_web_available` +- `ghostty_web_available` (visual renderer and semantic fallback) - `screenshot_viable` -If these fail in CI or a container, install Chromium during setup and make sure the cache is readable by the process running `agent-tty`. +If the browser-backed checks fail in CI or a container, install Chromium during setup and make sure the cache is readable by the process running `agent-tty`. If `libghostty_vt_available` is skipped or unavailable, semantic commands should fall back to `ghostty-web`; use `--renderer ghostty-web` to make that choice explicit. ## Isolated Homes @@ -77,10 +78,9 @@ or install a GitHub Release tarball as described in [`INSTALL.md`](./INSTALL.md) ## Reference Rendering Caveat -`ghostty-web` is the reference renderer for snapshots, screenshots, and replay video. -It gives repeatable artifacts for review and automation, but it does not guarantee exact native-terminal pixel parity. +`libghostty-vt` is the preferred default for semantic snapshots and render-backed waits when the optional native package is available. `ghostty-web` remains the reference visual renderer for screenshots and replay video, and it is the semantic fallback when native rendering is unavailable. -If a bug depends on a specific native terminal emulator, keep the `agent-tty` artifact as reference evidence and capture native-terminal evidence separately when needed. +These renderers give repeatable artifacts for review and automation, but they do not guarantee exact native-terminal pixel parity. If a bug depends on a specific native terminal emulator, keep the `agent-tty` artifact as reference evidence and capture native-terminal evidence separately when needed. ## Stray `%` at the End of Captured Output diff --git a/docs/USAGE.md b/docs/USAGE.md index c73ae00f..56a76a5d 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -190,8 +190,8 @@ The Wait Baseline fixes stale-match only. It does **not** fix echo-match: a `wai ## Screenshots And Recording Exports -Screenshots and WebM export use the `ghostty-web` reference renderer through Playwright/Chromium. -Run `doctor --json` first in new environments. +Screenshots and WebM export use the `ghostty-web` reference visual renderer through Playwright/Chromium. +Semantic `snapshot`, screen-hash, and render-backed `wait` paths prefer `libghostty-vt` when the optional native package is available and fall back to `ghostty-web` otherwise. Run `doctor --json` first in new environments. ```bash agent-tty screenshot --profile reference-dark --json @@ -202,6 +202,8 @@ agent-tty record export --format webm --timing accelerated --out ./ WebM export replays with recorded wall-clock timing by default. Pass `--timing accelerated` (idle gaps clamped to 400ms) or `--timing max-speed` for a time-compressed video. +Use `--renderer ghostty-web`, `AGENT_TTY_RENDERER=ghostty-web`, or Home `config.json` `{ "defaultRenderer": "ghostty-web" }` to force legacy all-browser rendering. Use `--renderer libghostty-vt` only when you intentionally want semantic and screenshot requests routed through the native backend; WebM requests still record `ghostty-web` as the actual video producer. + `ghostty-web` provides reference visual truth for reviewable artifacts; it does not promise exact pixel parity with native terminals. ## Isolation diff --git a/docs/prd/screen-hash/PRD.md b/docs/prd/screen-hash/PRD.md index ee154ddf..fc3fb2b4 100644 --- a/docs/prd/screen-hash/PRD.md +++ b/docs/prd/screen-hash/PRD.md @@ -31,7 +31,7 @@ Snapshot results and matched **Render Wait** results gain an optional **Screen H - Add an optional **Screen Hash** field — a lowercase 64-character SHA-256 hex digest — to the snapshot result (both structured and text formats) and to the matched render-wait result. - In scope: a **Batch Step** record for a matched **Render Wait** step also carries the **Screen Hash**, mirrored from that step's render-wait result, so a batch run exposes the same content identity per wait step that a standalone wait does. - The **Screen Hash** is the SHA-256 of the canonical visible-text string: the visible lines joined by newline, exactly as the host's screen-stability compare and the text matcher already build it. The shared canonical-text **definition** — `visibleLines[].text` joined by `\n`, sourced only from the snapshot (never `backend.getVisibleText()` or `cells[]`) — is unchanged by adding the hash. Cursor position, text styles, and scrollback are excluded. -- Converging the two renderer backends on one canonical screen form (Phase 1) intentionally changes the **default** `ghostty-web` backend's stability and text-wait **comparand** on screens with grapheme clusters, interior blank-cell gaps, or non-ASCII trailing characters: the canonical form is exactly `rows` lines, each decoded with full grapheme clusters with blank/zero cells as `' '`, then right-trimmed of trailing ASCII spaces (`0x20`) only. This is a deliberate, narrow change pinned by characterization tests, not a free behavior-preserving add; on plain ASCII screens the comparand is unchanged. +- Converging the two renderer backends on one canonical screen form (Phase 1) intentionally changed the then-default `ghostty-web` backend's stability and text-wait **comparand** on screens with grapheme clusters, interior blank-cell gaps, or non-ASCII trailing characters: the canonical form is exactly `rows` lines, each decoded with full grapheme clusters with blank/zero cells as `' '`, then right-trimmed of trailing ASCII spaces (`0x20`) only. This was a deliberate, narrow change pinned by characterization tests, not a free behavior-preserving add; on plain ASCII screens the comparand was unchanged. - Extract one shared canonical-screen-text helper and route the **Screen Hash**, the host **Screen Stability** compare, and the text **Render Wait** matcher through it, so the three share a single definition and cannot diverge. - The hash is keyed on whether a result holds an **observed** **Semantic Snapshot**, not on whether the wait matched. A result carries the **Screen Hash** of the snapshot it observed: a matched live wait, a snapshot capture, and the offline host-unreachable fallback that still observed a latest snapshot (even when it returns `matched: false` because the **Screen Stability** duration could not be proven offline). The hash is omitted only when no snapshot was observed: a live wait that times out, a consecutive-failure giveup, or a replay error throw. - Do not surface the **Screen Hash** on inspection or any path that does not already render a **Semantic Snapshot**; computing it must never force a renderer bootstrap that would not otherwise happen. @@ -55,7 +55,7 @@ Good tests assert external behavior, not implementation details. - A styled or per-cell hash. Transient style churn would make such a hash flap; the **Screen Hash** is text-content identity only. - Pixel-level identity, and any **Screen Hash** on the **Screenshot Result**. A **Screenshot Result** carries only its pixel `sha256`; the content hash lives on the snapshot and wait results. The **Screen Hash** is the semantic counterpart to the pixel digest and the two are not interchangeable. - New wait semantics built on the hash (for example, "wait until the screen content changes"). v1 only exposes the field; any hash-driven wait is future scope. -- Any change to the screen-stability behavior **beyond** the Phase 1 renderer-convergence change described in the Implementation Decisions. The canonical-text definition and the shared single-source unify are behavior-preserving; the only intended behavior change is the default `ghostty-web` backend's comparand on grapheme / interior-gap / non-ASCII-trailing screens, pinned by characterization tests. No new wait semantics are added. +- Any change to the screen-stability behavior **beyond** the Phase 1 renderer-convergence change described in the Implementation Decisions. The canonical-text definition and the shared single-source unify are behavior-preserving; the only intended behavior change was the then-default `ghostty-web` backend's comparand on grapheme / interior-gap / non-ASCII-trailing screens, pinned by characterization tests. No new wait semantics are added. ## Further Notes diff --git a/dogfood/20260616-default-semantic-renderer/README.md b/dogfood/20260616-default-semantic-renderer/README.md new file mode 100644 index 00000000..d418adc2 --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/README.md @@ -0,0 +1,57 @@ +# Default semantic renderer proof bundle + +This bundle proves the split renderer default: semantic actions (`wait`, `snapshot`, and screen-hash producing paths) now default to `libghostty-vt` when the optional native package is available, while visual artifacts still default to `ghostty-web`. + +## What this proves + +- `expected-renderer.json` records the automatic semantic renderer detected in this workspace. In this capture it is `libghostty-vt`. +- `default-wait.json` was captured without `--renderer`, exercising the automatic semantic render-wait path. +- `default-snapshot.json` was captured without `--renderer`; `default-snapshot-artifact.json` records the snapshot artifact metadata with `rendererBackend: "libghostty-vt"`. +- `default-screenshot.json` was captured without `--renderer` and reports `result.rendererBackend: "ghostty-web"`; the PNG is copied to `screenshots/default-screenshot.png`. +- `default-webm.json` was captured without `--renderer` and reports `result.metadata.rendererBackend: "ghostty-web"`; the WebM is copied to `videos/default-webm.webm`. +- `explicit-ghostty-web-snapshot.json` proves the legacy override path; `explicit-ghostty-web-snapshot-artifact.json` records `rendererBackend: "ghostty-web"`. +- `explicit-libghostty-vt-screenshot.json` proves explicit native screenshot requests still produce honest `ghostty-web` PNG metadata via fallback. +- `explicit-libghostty-vt-webm.json` proves explicit native WebM requests are accepted while the actual video producer remains `ghostty-web`. +- `default-cast.json` and `recordings/default.cast` keep a terminal recording of the session. + +## Bundle contents + +- `commands.sh` — self-contained replay script using an isolated `AGENT_TTY_HOME` from `mktemp -d` and the repo-local CLI via `npx tsx src/cli/main.ts`. +- `environment.txt`, `version.json`, `doctor.json`, `expected-renderer.json` — environment and capability evidence. +- `default-*.json` — default renderer behavior envelopes. +- `explicit-*.json` — explicit override behavior envelopes. +- `*-artifact.json` and `artifact-manifest.json` — artifact metadata used to verify semantic snapshot producers. +- `screenshots/*.png` — visual proof artifacts. +- `videos/*.webm` — video proof artifacts. +- `recordings/default.cast` — asciicast recording. +- `artifact-file-info.txt` and `artifact-sha256.txt` — file type and checksum evidence for copied artifacts. + +## How to reproduce + +From the repository root: + +```bash +bash dogfood/20260616-default-semantic-renderer/commands.sh +``` + +The script requires `git`, `jq`, `node`, `npm`, `npx`, and the installed project dependencies. It never writes to `~/.agent-tty`; every CLI command uses the temporary `AGENT_TTY_HOME` created at startup. + +## Reviewer checks + +```bash +jq -r '.expectedSemanticRenderer' dogfood/20260616-default-semantic-renderer/expected-renderer.json +jq -r '.metadata.rendererBackend' \ + dogfood/20260616-default-semantic-renderer/default-snapshot-artifact.json \ + dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot-artifact.json +jq -r '.result.rendererBackend' \ + dogfood/20260616-default-semantic-renderer/default-screenshot.json \ + dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-screenshot.json +jq -r '.result.metadata.rendererBackend' \ + dogfood/20260616-default-semantic-renderer/default-webm.json \ + dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-webm.json +file dogfood/20260616-default-semantic-renderer/screenshots/*.png +file dogfood/20260616-default-semantic-renderer/videos/*.webm +file dogfood/20260616-default-semantic-renderer/recordings/*.cast +``` + +Expected output in this workspace: semantic snapshot metadata starts with `libghostty-vt`, the explicit legacy snapshot reports `ghostty-web`, and all screenshot/WebM producer fields report `ghostty-web`. diff --git a/dogfood/20260616-default-semantic-renderer/artifact-file-info.txt b/dogfood/20260616-default-semantic-renderer/artifact-file-info.txt new file mode 100644 index 00000000..9fc754fd --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/artifact-file-info.txt @@ -0,0 +1,5 @@ +/home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/screenshots/default-screenshot.png: PNG image data, 640 x 384, 8-bit/color RGB, non-interlaced +/home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/screenshots/explicit-libghostty-vt-screenshot.png: PNG image data, 640 x 384, 8-bit/color RGB, non-interlaced +/home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/videos/default-webm.webm: WebM +/home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/videos/explicit-libghostty-vt-webm.webm: WebM +/home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/recordings/default.cast: JSON data diff --git a/dogfood/20260616-default-semantic-renderer/artifact-manifest.json b/dogfood/20260616-default-semantic-renderer/artifact-manifest.json new file mode 100644 index 00000000..5e892480 --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/artifact-manifest.json @@ -0,0 +1,143 @@ +{ + "version": 1, + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "artifacts": [ + { + "id": "01KV851Q0MS2YQW4MTWXYNJBBD", + "kind": "snapshot", + "filename": "snapshot-6-structured.json", + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "capturedAtSeq": 6, + "createdAt": "2026-06-16T12:03:40.693Z", + "metadata": { + "format": "structured", + "rendererBackend": "libghostty-vt", + "cols": 80, + "rows": 24, + "cursorRow": 5, + "cursorCol": 7 + } + }, + { + "id": "01KV851RXQP4ZQRBXFNHHNSQTM", + "kind": "screenshot", + "filename": "screenshot-6-reference-dark.png", + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "capturedAtSeq": 6, + "createdAt": "2026-06-16T12:03:42.647Z", + "sha256": "5864fb7aed3726a2a88ca79a7ce5db53159c35fcd0ca7982b3e15c367f173af7", + "metadata": { + "profileName": "reference-dark", + "cols": 80, + "rows": 24, + "pngSizeBytes": 13319, + "cursorVisible": false, + "rendererBackend": "ghostty-web", + "pixelWidth": 640, + "pixelHeight": 384, + "renderProfileHash": "8ffed6af301ec7c0e6b69599c3be0d1d12096f9fcdfc59d0bbb4cc474d64c53d" + } + }, + { + "id": "01KV851TCV0Q3J1QS8KR93E6FS", + "kind": "snapshot", + "filename": "snapshot-6-text.json", + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "capturedAtSeq": 6, + "createdAt": "2026-06-16T12:03:44.156Z", + "metadata": { + "format": "text", + "rendererBackend": "ghostty-web", + "cols": 80, + "rows": 24, + "cursorRow": 5, + "cursorCol": 7 + } + }, + { + "id": "01KV851W9NV6PWW8TP0S8CH5XF", + "kind": "screenshot", + "filename": "screenshot-6-reference-dark.png", + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "capturedAtSeq": 6, + "createdAt": "2026-06-16T12:03:46.101Z", + "sha256": "5864fb7aed3726a2a88ca79a7ce5db53159c35fcd0ca7982b3e15c367f173af7", + "metadata": { + "profileName": "reference-dark", + "cols": 80, + "rows": 24, + "pngSizeBytes": 13319, + "cursorVisible": false, + "rendererBackend": "ghostty-web", + "pixelWidth": 640, + "pixelHeight": 384, + "renderProfileHash": "8ffed6af301ec7c0e6b69599c3be0d1d12096f9fcdfc59d0bbb4cc474d64c53d" + } + }, + { + "id": "01KV8524W53CY3E89V0RFESPH3", + "kind": "video", + "filename": "recording-14-webm.webm", + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "capturedAtSeq": 14, + "createdAt": "2026-06-16T12:03:54.886Z", + "sha256": "6c1ae7c446c1df85c85027ab74ecbd270e23398e5cf48cd91c73cd37b8bfb0eb", + "bytes": 49225, + "metadata": { + "format": "webm", + "outputPath": "/tmp/agent-tty-default-renderer.w26eUX/sessions/01KV851DZH93PX9FQA3C199TSS/artifacts/recording-14-webm.webm", + "width": 80, + "height": 24, + "profileName": "reference-dark", + "renderProfileHash": "8ffed6af301ec7c0e6b69599c3be0d1d12096f9fcdfc59d0bbb4cc474d64c53d", + "timingMode": "accelerated", + "rendererBackend": "ghostty-web", + "outputEventCount": 10, + "resizeEventCount": 0 + } + }, + { + "id": "01KV8529Z6X3AE5GTCFX277DRE", + "kind": "video", + "filename": "recording-14-webm.webm", + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "capturedAtSeq": 14, + "createdAt": "2026-06-16T12:04:00.103Z", + "sha256": "26feddb53310eaf020503b6f75e73b96d731995d6fa104aa046601c3a94d0328", + "bytes": 49695, + "metadata": { + "format": "webm", + "outputPath": "/tmp/agent-tty-default-renderer.w26eUX/sessions/01KV851DZH93PX9FQA3C199TSS/artifacts/recording-14-webm.webm", + "width": 80, + "height": 24, + "profileName": "reference-dark", + "renderProfileHash": "8ffed6af301ec7c0e6b69599c3be0d1d12096f9fcdfc59d0bbb4cc474d64c53d", + "timingMode": "accelerated", + "rendererBackend": "ghostty-web", + "outputEventCount": 10, + "resizeEventCount": 0 + } + }, + { + "id": "01KV852BE8YJKSQSWSJX6R1AVH", + "kind": "recording", + "filename": "recording-14-asciicast.cast", + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "capturedAtSeq": 14, + "createdAt": "2026-06-16T12:04:01.609Z", + "sha256": "f2c09e7d3a884898e05ed1100b98c0a1063d75f0a38e9011acd51e5e2190d5dc", + "bytes": 667, + "metadata": { + "format": "asciicast", + "outputPath": "/tmp/agent-tty-default-renderer.w26eUX/sessions/01KV851DZH93PX9FQA3C199TSS/artifacts/recording-14-asciicast.cast", + "width": 80, + "height": 24, + "title": "01KV851DZH93PX9FQA3C199TSS", + "timestamp": 1781611412, + "outputEventCount": 10, + "resizeEventCount": 0, + "markerCount": 0 + } + } + ] +} diff --git a/dogfood/20260616-default-semantic-renderer/artifact-sha256.txt b/dogfood/20260616-default-semantic-renderer/artifact-sha256.txt new file mode 100644 index 00000000..4f97d918 --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/artifact-sha256.txt @@ -0,0 +1,5 @@ +5864fb7aed3726a2a88ca79a7ce5db53159c35fcd0ca7982b3e15c367f173af7 /home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/screenshots/default-screenshot.png +5864fb7aed3726a2a88ca79a7ce5db53159c35fcd0ca7982b3e15c367f173af7 /home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/screenshots/explicit-libghostty-vt-screenshot.png +6c1ae7c446c1df85c85027ab74ecbd270e23398e5cf48cd91c73cd37b8bfb0eb /home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/videos/default-webm.webm +26feddb53310eaf020503b6f75e73b96d731995d6fa104aa046601c3a94d0328 /home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/videos/explicit-libghostty-vt-webm.webm +f2c09e7d3a884898e05ed1100b98c0a1063d75f0a38e9011acd51e5e2190d5dc /home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/recordings/default.cast diff --git a/dogfood/20260616-default-semantic-renderer/commands.sh b/dogfood/20260616-default-semantic-renderer/commands.sh new file mode 100755 index 00000000..8b27acae --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/commands.sh @@ -0,0 +1,223 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel)" +BUNDLE_DIR="$SCRIPT_DIR" +SCREENSHOTS_DIR="$BUNDLE_DIR/screenshots" +VIDEOS_DIR="$BUNDLE_DIR/videos" +RECORDINGS_DIR="$BUNDLE_DIR/recordings" +CLI=(npx tsx src/cli/main.ts --timeout-ms 120000) +FIXTURE=(npx tsx test/fixtures/apps/hello-prompt/main.ts) +PROMPT_TEXT='READY>' +ECHO_TEXT='Default semantic renderer proof' +AGENT_TTY_HOME="$(mktemp -d -t agent-tty-default-renderer.XXXXXX)" +export AGENT_TTY_HOME +SESSION_ID='' + +require_command() { + command -v "$1" >/dev/null 2>&1 || { + printf 'missing required command: %s\n' "$1" >&2 + exit 1 + } +} + +assert_file_nonempty() { + local path="$1" + [[ -s "$path" ]] || { + printf 'expected non-empty file: %s\n' "$path" >&2 + exit 1 + } +} + +run_json_file() { + local output_path="$1" + shift + local tmp_path="$output_path.tmp" + "$@" > "$tmp_path" + jq . "$tmp_path" > "$output_path" + rm -f "$tmp_path" + jq -e '.ok == true' "$output_path" >/dev/null +} + +capture_json_var() { + local __resultvar="$1" + shift + local raw_json + local pretty_json + raw_json="$($@)" + pretty_json="$(printf '%s\n' "$raw_json" | jq .)" + printf '%s\n' "$pretty_json" | jq -e '.ok == true' >/dev/null + printf -v "$__resultvar" '%s' "$pretty_json" +} + +run_json_check_only() { + "$@" | jq -e '.ok == true' >/dev/null +} + +session_dir() { + printf '%s/sessions/%s\n' "$AGENT_TTY_HOME" "$SESSION_ID" +} + +artifact_manifest_path() { + printf '%s/artifacts/manifest.json\n' "$(session_dir)" +} + +copy_artifact_manifest() { + jq . "$(artifact_manifest_path)" > "$BUNDLE_DIR/artifact-manifest.json" +} + +write_latest_artifact() { + local kind="$1" + local output_path="$2" + jq --arg kind "$kind" ' + .artifacts | map(select(.kind == $kind)) | last + ' "$(artifact_manifest_path)" > "$output_path" +} + +assert_latest_artifact_backend() { + local kind="$1" + local expected_backend="$2" + jq -e --arg kind "$kind" --arg backend "$expected_backend" ' + (.artifacts | map(select(.kind == $kind)) | last | .metadata.rendererBackend) == $backend + ' "$(artifact_manifest_path)" >/dev/null +} + +cleanup() { + local exit_code=$? + set +e + if [[ -n "${SESSION_ID:-}" ]]; then + "${CLI[@]}" --home "$AGENT_TTY_HOME" destroy "$SESSION_ID" --json >/dev/null 2>&1 || true + fi + if [[ -n "${AGENT_TTY_HOME:-}" && -d "${AGENT_TTY_HOME:-}" ]]; then + rm -rf "$AGENT_TTY_HOME" + fi + exit "$exit_code" +} +trap cleanup EXIT + +require_command git +require_command jq +require_command node +require_command npm +require_command npx +require_command uname + +mkdir -p "$SCREENSHOTS_DIR" "$VIDEOS_DIR" "$RECORDINGS_DIR" +rm -f "$BUNDLE_DIR"/*.json "$BUNDLE_DIR/environment.txt" +rm -f "$SCREENSHOTS_DIR"/*.png "$VIDEOS_DIR"/*.webm "$RECORDINGS_DIR"/*.cast + +cd "$REPO_ROOT" + +EXPECTED_SEMANTIC_RENDERER="$({ + node --input-type=module <<'NODE' +try { + const mod = await import('@coder/libghostty-vt-node'); + if (typeof mod.createTerminal === 'function') { + console.log('libghostty-vt'); + } else { + console.log('ghostty-web'); + } +} catch { + console.log('ghostty-web'); +} +NODE +} | tail -n 1)" +printf '{"expectedSemanticRenderer":"%s"}\n' "$EXPECTED_SEMANTIC_RENDERER" | jq . > "$BUNDLE_DIR/expected-renderer.json" + +{ + printf '$ node --version\n%s\n\n' "$(node --version)" + printf '$ npm --version\n%s\n\n' "$(npm --version)" + printf '$ git rev-parse HEAD\n%s\n\n' "$(git rev-parse HEAD)" + printf '$ git log --oneline -n 1\n%s\n\n' "$(git log --oneline -n 1)" + printf '$ uname -a\n%s\n\n' "$(uname -a)" + printf '$ expected semantic renderer\n%s\n\n' "$EXPECTED_SEMANTIC_RENDERER" + printf '$ npx tsx src/cli/main.ts version --json\n' + "${CLI[@]}" version --json | jq . + printf '\n$ npx tsx src/cli/main.ts doctor --json\n' + "${CLI[@]}" --home "$AGENT_TTY_HOME" doctor --json | jq . +} > "$BUNDLE_DIR/environment.txt" + +run_json_file "$BUNDLE_DIR/version.json" "${CLI[@]}" version --json +run_json_file "$BUNDLE_DIR/doctor.json" "${CLI[@]}" --home "$AGENT_TTY_HOME" doctor --json + +CREATE_JSON='' +capture_json_var \ + CREATE_JSON \ + "${CLI[@]}" --home "$AGENT_TTY_HOME" create --json --cwd "$REPO_ROOT" \ + --cols 80 --rows 24 --name default-semantic-renderer -- "${FIXTURE[@]}" +SESSION_ID="$(printf '%s\n' "$CREATE_JSON" | jq -er '.result.sessionId')" +printf '%s\n' "$CREATE_JSON" > "$BUNDLE_DIR/create.json" + +run_json_check_only "${CLI[@]}" --home "$AGENT_TTY_HOME" wait "$SESSION_ID" --json --text "$PROMPT_TEXT" --timeout 10000 +run_json_check_only "${CLI[@]}" --home "$AGENT_TTY_HOME" type "$SESSION_ID" --json "$ECHO_TEXT" +run_json_check_only "${CLI[@]}" --home "$AGENT_TTY_HOME" send-keys "$SESSION_ID" --json Enter +run_json_file "$BUNDLE_DIR/default-wait.json" \ + "${CLI[@]}" --home "$AGENT_TTY_HOME" wait "$SESSION_ID" --json --text "ECHO: $ECHO_TEXT" --timeout 10000 +run_json_check_only "${CLI[@]}" --home "$AGENT_TTY_HOME" wait "$SESSION_ID" --json --screen-stable-ms 250 --timeout 10000 + +run_json_file "$BUNDLE_DIR/default-snapshot.json" \ + "${CLI[@]}" --home "$AGENT_TTY_HOME" snapshot "$SESSION_ID" --format structured --json +assert_latest_artifact_backend snapshot "$EXPECTED_SEMANTIC_RENDERER" +write_latest_artifact snapshot "$BUNDLE_DIR/default-snapshot-artifact.json" + +run_json_file "$BUNDLE_DIR/default-screenshot.json" \ + "${CLI[@]}" --home "$AGENT_TTY_HOME" screenshot "$SESSION_ID" --hide-cursor --json +jq -e '.result.rendererBackend == "ghostty-web"' "$BUNDLE_DIR/default-screenshot.json" >/dev/null +DEFAULT_SCREENSHOT_SOURCE="$(jq -er '.result.artifactPath' "$BUNDLE_DIR/default-screenshot.json")" +assert_file_nonempty "$DEFAULT_SCREENSHOT_SOURCE" +cp "$DEFAULT_SCREENSHOT_SOURCE" "$SCREENSHOTS_DIR/default-screenshot.png" +assert_file_nonempty "$SCREENSHOTS_DIR/default-screenshot.png" + +run_json_file "$BUNDLE_DIR/explicit-ghostty-web-snapshot.json" \ + "${CLI[@]}" --home "$AGENT_TTY_HOME" --renderer ghostty-web snapshot "$SESSION_ID" --format text --json +assert_latest_artifact_backend snapshot ghostty-web +write_latest_artifact snapshot "$BUNDLE_DIR/explicit-ghostty-web-snapshot-artifact.json" + +run_json_file "$BUNDLE_DIR/explicit-libghostty-vt-screenshot.json" \ + "${CLI[@]}" --home "$AGENT_TTY_HOME" --renderer libghostty-vt screenshot "$SESSION_ID" --hide-cursor --json +jq -e '.result.rendererBackend == "ghostty-web"' "$BUNDLE_DIR/explicit-libghostty-vt-screenshot.json" >/dev/null +EXPLICIT_SCREENSHOT_SOURCE="$(jq -er '.result.artifactPath' "$BUNDLE_DIR/explicit-libghostty-vt-screenshot.json")" +assert_file_nonempty "$EXPLICIT_SCREENSHOT_SOURCE" +cp "$EXPLICIT_SCREENSHOT_SOURCE" "$SCREENSHOTS_DIR/explicit-libghostty-vt-screenshot.png" +assert_file_nonempty "$SCREENSHOTS_DIR/explicit-libghostty-vt-screenshot.png" + +run_json_check_only "${CLI[@]}" --home "$AGENT_TTY_HOME" type "$SESSION_ID" --json 'exit' +run_json_check_only "${CLI[@]}" --home "$AGENT_TTY_HOME" send-keys "$SESSION_ID" --json Enter +run_json_check_only "${CLI[@]}" --home "$AGENT_TTY_HOME" wait "$SESSION_ID" --json --exit --timeout 10000 + +run_json_file "$BUNDLE_DIR/default-webm.json" \ + "${CLI[@]}" --home "$AGENT_TTY_HOME" record export "$SESSION_ID" --format webm --timing accelerated --json +jq -e '.result.metadata.rendererBackend == "ghostty-web"' "$BUNDLE_DIR/default-webm.json" >/dev/null +DEFAULT_WEBM_SOURCE="$(jq -er '.result.artifactPath' "$BUNDLE_DIR/default-webm.json")" +assert_file_nonempty "$DEFAULT_WEBM_SOURCE" +cp "$DEFAULT_WEBM_SOURCE" "$VIDEOS_DIR/default-webm.webm" +assert_file_nonempty "$VIDEOS_DIR/default-webm.webm" + +run_json_file "$BUNDLE_DIR/explicit-libghostty-vt-webm.json" \ + "${CLI[@]}" --home "$AGENT_TTY_HOME" --renderer libghostty-vt record export "$SESSION_ID" --format webm --timing accelerated --json +jq -e '.result.metadata.rendererBackend == "ghostty-web"' "$BUNDLE_DIR/explicit-libghostty-vt-webm.json" >/dev/null +EXPLICIT_WEBM_SOURCE="$(jq -er '.result.artifactPath' "$BUNDLE_DIR/explicit-libghostty-vt-webm.json")" +assert_file_nonempty "$EXPLICIT_WEBM_SOURCE" +cp "$EXPLICIT_WEBM_SOURCE" "$VIDEOS_DIR/explicit-libghostty-vt-webm.webm" +assert_file_nonempty "$VIDEOS_DIR/explicit-libghostty-vt-webm.webm" + +run_json_file "$BUNDLE_DIR/default-cast.json" \ + "${CLI[@]}" --home "$AGENT_TTY_HOME" record export "$SESSION_ID" --format asciicast --json +CAST_SOURCE="$(jq -er '.result.artifactPath' "$BUNDLE_DIR/default-cast.json")" +assert_file_nonempty "$CAST_SOURCE" +cp "$CAST_SOURCE" "$RECORDINGS_DIR/default.cast" +assert_file_nonempty "$RECORDINGS_DIR/default.cast" + +run_json_file "$BUNDLE_DIR/inspect.json" "${CLI[@]}" --home "$AGENT_TTY_HOME" inspect "$SESSION_ID" --json +copy_artifact_manifest + +file "$SCREENSHOTS_DIR"/*.png > "$BUNDLE_DIR/artifact-file-info.txt" +file "$VIDEOS_DIR"/*.webm >> "$BUNDLE_DIR/artifact-file-info.txt" +file "$RECORDINGS_DIR"/*.cast >> "$BUNDLE_DIR/artifact-file-info.txt" +sha256sum "$SCREENSHOTS_DIR"/*.png "$VIDEOS_DIR"/*.webm "$RECORDINGS_DIR"/*.cast > "$BUNDLE_DIR/artifact-sha256.txt" + +run_json_check_only "${CLI[@]}" --home "$AGENT_TTY_HOME" destroy "$SESSION_ID" --json +SESSION_ID='' + +printf 'dogfood bundle written to %s\n' "$BUNDLE_DIR" diff --git a/dogfood/20260616-default-semantic-renderer/create.json b/dogfood/20260616-default-semantic-renderer/create.json new file mode 100644 index 00000000..74ab2fca --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/create.json @@ -0,0 +1,12 @@ +{ + "ok": true, + "command": "create", + "timestamp": "2026-06-16T12:03:32.281Z", + "result": { + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "createdAt": "2026-06-16T12:03:31.444Z", + "cols": 80, + "rows": 24, + "shell": "/bin/bash" + } +} diff --git a/dogfood/20260616-default-semantic-renderer/default-cast.json b/dogfood/20260616-default-semantic-renderer/default-cast.json new file mode 100644 index 00000000..4665c13d --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/default-cast.json @@ -0,0 +1,23 @@ +{ + "ok": true, + "command": "record export", + "timestamp": "2026-06-16T12:04:01.613Z", + "result": { + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "format": "asciicast", + "artifactPath": "/tmp/agent-tty-default-renderer.w26eUX/sessions/01KV851DZH93PX9FQA3C199TSS/artifacts/recording-14-asciicast.cast", + "bytes": 667, + "sha256": "f2c09e7d3a884898e05ed1100b98c0a1063d75f0a38e9011acd51e5e2190d5dc", + "capturedAtSeq": 14, + "durationMs": 16177, + "metadata": { + "width": 80, + "height": 24, + "title": "01KV851DZH93PX9FQA3C199TSS", + "timestamp": 1781611412, + "outputEventCount": 10, + "resizeEventCount": 0, + "markerCount": 0 + } + } +} diff --git a/dogfood/20260616-default-semantic-renderer/default-screenshot.json b/dogfood/20260616-default-semantic-renderer/default-screenshot.json new file mode 100644 index 00000000..d2c52f83 --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/default-screenshot.json @@ -0,0 +1,20 @@ +{ + "ok": true, + "command": "screenshot", + "timestamp": "2026-06-16T12:03:42.650Z", + "result": { + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "capturedAtSeq": 6, + "profileName": "reference-dark", + "cols": 80, + "rows": 24, + "artifactPath": "/tmp/agent-tty-default-renderer.w26eUX/sessions/01KV851DZH93PX9FQA3C199TSS/artifacts/screenshot-6-reference-dark.png", + "pngSizeBytes": 13319, + "cursorVisible": false, + "rendererBackend": "ghostty-web", + "pixelWidth": 640, + "pixelHeight": 384, + "sha256": "5864fb7aed3726a2a88ca79a7ce5db53159c35fcd0ca7982b3e15c367f173af7", + "renderProfileHash": "8ffed6af301ec7c0e6b69599c3be0d1d12096f9fcdfc59d0bbb4cc474d64c53d" + } +} diff --git a/dogfood/20260616-default-semantic-renderer/default-snapshot-artifact.json b/dogfood/20260616-default-semantic-renderer/default-snapshot-artifact.json new file mode 100644 index 00000000..8af3a90b --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/default-snapshot-artifact.json @@ -0,0 +1,16 @@ +{ + "id": "01KV851Q0MS2YQW4MTWXYNJBBD", + "kind": "snapshot", + "filename": "snapshot-6-structured.json", + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "capturedAtSeq": 6, + "createdAt": "2026-06-16T12:03:40.693Z", + "metadata": { + "format": "structured", + "rendererBackend": "libghostty-vt", + "cols": 80, + "rows": 24, + "cursorRow": 5, + "cursorCol": 7 + } +} diff --git a/dogfood/20260616-default-semantic-renderer/default-snapshot.json b/dogfood/20260616-default-semantic-renderer/default-snapshot.json new file mode 100644 index 00000000..64e2b57a --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/default-snapshot.json @@ -0,0 +1,114 @@ +{ + "ok": true, + "command": "snapshot", + "timestamp": "2026-06-16T12:03:40.697Z", + "result": { + "format": "structured", + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "capturedAtSeq": 6, + "cols": 80, + "rows": 24, + "cursorRow": 5, + "cursorCol": 7, + "isAltScreen": false, + "visibleLines": [ + { + "row": 0, + "text": "READY> (node:2465832) [DEP0205] DeprecationWarning: `module.register()` is depre" + }, + { + "row": 1, + "text": "cated. Use `module.registerHooks()` instead." + }, + { + "row": 2, + "text": "(Use `node --trace-deprecation ...` to show where the warning was created)" + }, + { + "row": 3, + "text": "Default semantic renderer proof" + }, + { + "row": 4, + "text": "ECHO: Default semantic renderer proof" + }, + { + "row": 5, + "text": "READY>" + }, + { + "row": 6, + "text": "" + }, + { + "row": 7, + "text": "" + }, + { + "row": 8, + "text": "" + }, + { + "row": 9, + "text": "" + }, + { + "row": 10, + "text": "" + }, + { + "row": 11, + "text": "" + }, + { + "row": 12, + "text": "" + }, + { + "row": 13, + "text": "" + }, + { + "row": 14, + "text": "" + }, + { + "row": 15, + "text": "" + }, + { + "row": 16, + "text": "" + }, + { + "row": 17, + "text": "" + }, + { + "row": 18, + "text": "" + }, + { + "row": 19, + "text": "" + }, + { + "row": 20, + "text": "" + }, + { + "row": 21, + "text": "" + }, + { + "row": 22, + "text": "" + }, + { + "row": 23, + "text": "" + } + ], + "screenHash": "0feb80f00d88c52b4a000f6b1f718a63c6136bf8c9c2d90cc85c123cdf36f229" + } +} diff --git a/dogfood/20260616-default-semantic-renderer/default-wait.json b/dogfood/20260616-default-semantic-renderer/default-wait.json new file mode 100644 index 00000000..1d0fd6e9 --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/default-wait.json @@ -0,0 +1,14 @@ +{ + "ok": true, + "command": "wait", + "timestamp": "2026-06-16T12:03:37.563Z", + "result": { + "matched": true, + "timedOut": false, + "matchedText": "ECHO: Default semantic renderer proof", + "cursorRow": 5, + "cursorCol": 7, + "capturedAtSeq": 6, + "screenHash": "0feb80f00d88c52b4a000f6b1f718a63c6136bf8c9c2d90cc85c123cdf36f229" + } +} diff --git a/dogfood/20260616-default-semantic-renderer/default-webm.json b/dogfood/20260616-default-semantic-renderer/default-webm.json new file mode 100644 index 00000000..8678876d --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/default-webm.json @@ -0,0 +1,24 @@ +{ + "ok": true, + "command": "record export", + "timestamp": "2026-06-16T12:03:54.892Z", + "result": { + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "format": "webm", + "artifactPath": "/tmp/agent-tty-default-renderer.w26eUX/sessions/01KV851DZH93PX9FQA3C199TSS/artifacts/recording-14-webm.webm", + "bytes": 49225, + "sha256": "6c1ae7c446c1df85c85027ab74ecbd270e23398e5cf48cd91c73cd37b8bfb0eb", + "capturedAtSeq": 14, + "durationMs": 16177, + "metadata": { + "width": 80, + "height": 24, + "profileName": "reference-dark", + "renderProfileHash": "8ffed6af301ec7c0e6b69599c3be0d1d12096f9fcdfc59d0bbb4cc474d64c53d", + "timingMode": "accelerated", + "rendererBackend": "ghostty-web", + "outputEventCount": 10, + "resizeEventCount": 0 + } + } +} diff --git a/dogfood/20260616-default-semantic-renderer/doctor.json b/dogfood/20260616-default-semantic-renderer/doctor.json new file mode 100644 index 00000000..ab2be5cb --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/doctor.json @@ -0,0 +1,142 @@ +{ + "ok": true, + "command": "doctor", + "timestamp": "2026-06-16T12:03:30.112Z", + "result": { + "ok": true, + "checks": { + "environment": [ + { + "name": "node-runtime", + "status": "pass", + "message": "Node 26.2.0 ok", + "durationMs": 0 + }, + { + "name": "cwd-access", + "status": "pass", + "message": "cwd read/write: /home/coder/.mux/src/agent-terminal/vt-impl-5ds6", + "durationMs": 2 + }, + { + "name": "temp-dir", + "status": "pass", + "message": "temp dir ok: /tmp", + "durationMs": 1 + }, + { + "name": "home_isolation", + "status": "pass", + "message": "agent-tty home is isolated from system home: /tmp/agent-tty-default-renderer.w26eUX", + "durationMs": 1 + }, + { + "name": "home-writable", + "status": "pass", + "message": "home writable: /tmp/agent-tty-default-renderer.w26eUX", + "durationMs": 1 + }, + { + "name": "pty-spawn", + "status": "pass", + "message": "spawned /home/coder/.local/share/mise/installs/node/26.2.0/bin/node", + "durationMs": 37 + }, + { + "name": "socket-viable", + "status": "pass", + "message": "socket ok: /tmp/agent-tty/9cf86873/3806c31448a6", + "durationMs": 2 + }, + { + "name": "artifact-atomicity", + "status": "pass", + "message": "atomic rename ok: /tmp/agent-tty-default-renderer.w26eUX/sessions/doctor-2464905-mqglflt3-3/artifacts", + "durationMs": 2 + }, + { + "name": "event-log-writable", + "status": "pass", + "message": "append ok: /tmp/agent-tty-default-renderer.w26eUX/sessions/doctor-2464905-mqglflt4-5/events.jsonl", + "durationMs": 1 + } + ], + "renderer": [ + { + "name": "playwright_available", + "status": "pass", + "message": "available", + "durationMs": 1 + }, + { + "name": "browser_cache_accessible", + "status": "pass", + "message": "browser cache accessible: /home/coder/.cache/ms-playwright", + "durationMs": 0 + }, + { + "name": "browser_launch", + "status": "pass", + "message": "chromium launches", + "durationMs": 107 + }, + { + "name": "ghostty_web_available", + "status": "pass", + "message": "WASM available", + "durationMs": 131 + }, + { + "name": "screenshot_viable", + "status": "pass", + "message": "viable", + "durationMs": 165 + }, + { + "name": "libghostty_vt_available", + "status": "pass", + "message": "@coder/libghostty-vt-node exposes createTerminal()", + "durationMs": 1 + } + ] + }, + "capabilities": [ + { + "name": "snapshot", + "status": "available", + "reason": "libghostty-vt semantic renderer available", + "detail": "@coder/libghostty-vt-node exposes createTerminal()" + }, + { + "name": "wait", + "status": "available", + "reason": "libghostty-vt semantic renderer available", + "detail": "@coder/libghostty-vt-node exposes createTerminal()" + }, + { + "name": "screenshot", + "status": "available", + "reason": "renderer smoke checks passed", + "detail": "playwright_available: available; browser_launch: chromium launches; ghostty_web_available: WASM available; screenshot_viable: viable" + }, + { + "name": "record-export-asciicast", + "status": "available", + "reason": "built-in capability", + "detail": "available without external renderer dependencies" + }, + { + "name": "record-export-webm", + "status": "available", + "reason": "browser-backed export dependencies available", + "detail": "playwright_available: available; browser_launch: chromium launches; ghostty_web_available: WASM available" + }, + { + "name": "dashboard", + "status": "available", + "reason": "libghostty-vt native module available", + "detail": "@coder/libghostty-vt-node exposes createTerminal()" + } + ] + } +} diff --git a/dogfood/20260616-default-semantic-renderer/environment.txt b/dogfood/20260616-default-semantic-renderer/environment.txt new file mode 100644 index 00000000..9d1f161d --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/environment.txt @@ -0,0 +1,207 @@ +$ node --version +v26.2.0 + +$ npm --version +11.13.0 + +$ git rev-parse HEAD +d27bb8d34e97e54019bb8f7b4dd04195ca20718c + +$ git log --oneline -n 1 +d27bb8d fix: sync README version in release PRs (#156) + +$ uname -a +Linux aaaaaaa 6.8.0-124-generic #124-Ubuntu SMP PREEMPT_DYNAMIC Tue May 26 13:00:45 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux + +$ expected semantic renderer +libghostty-vt + +$ npx tsx src/cli/main.ts version --json +{ + "ok": true, + "command": "version", + "timestamp": "2026-06-16T12:03:25.596Z", + "result": { + "cliVersion": "0.4.3", + "protocolVersion": "0.1.0", + "rendererBackends": [ + "ghostty-web", + "libghostty-vt" + ], + "runtime": { + "node": "v26.2.0", + "platform": "linux", + "arch": "x64" + }, + "capabilities": [ + { + "name": "snapshot", + "status": "available" + }, + { + "name": "wait", + "status": "available" + }, + { + "name": "screenshot", + "status": "available" + }, + { + "name": "record-export-asciicast", + "status": "available" + }, + { + "name": "record-export-webm", + "status": "available" + }, + { + "name": "dashboard", + "status": "available" + } + ] + } +} + +$ npx tsx src/cli/main.ts doctor --json +{ + "ok": true, + "command": "doctor", + "timestamp": "2026-06-16T12:03:27.189Z", + "result": { + "ok": true, + "checks": { + "environment": [ + { + "name": "node-runtime", + "status": "pass", + "message": "Node 26.2.0 ok", + "durationMs": 0 + }, + { + "name": "cwd-access", + "status": "pass", + "message": "cwd read/write: /home/coder/.mux/src/agent-terminal/vt-impl-5ds6", + "durationMs": 2 + }, + { + "name": "temp-dir", + "status": "pass", + "message": "temp dir ok: /tmp", + "durationMs": 1 + }, + { + "name": "home_isolation", + "status": "pass", + "message": "agent-tty home is isolated from system home: /tmp/agent-tty-default-renderer.w26eUX", + "durationMs": 0 + }, + { + "name": "home-writable", + "status": "pass", + "message": "home writable: /tmp/agent-tty-default-renderer.w26eUX", + "durationMs": 1 + }, + { + "name": "pty-spawn", + "status": "pass", + "message": "spawned /home/coder/.local/share/mise/installs/node/26.2.0/bin/node", + "durationMs": 31 + }, + { + "name": "socket-viable", + "status": "pass", + "message": "socket ok: /tmp/agent-tty/9cf86873/8ea9a600aad8", + "durationMs": 3 + }, + { + "name": "artifact-atomicity", + "status": "pass", + "message": "atomic rename ok: /tmp/agent-tty-default-renderer.w26eUX/sessions/doctor-2464044-mqglfjkn-3/artifacts", + "durationMs": 1 + }, + { + "name": "event-log-writable", + "status": "pass", + "message": "append ok: /tmp/agent-tty-default-renderer.w26eUX/sessions/doctor-2464044-mqglfjko-5/events.jsonl", + "durationMs": 1 + } + ], + "renderer": [ + { + "name": "playwright_available", + "status": "pass", + "message": "available", + "durationMs": 1 + }, + { + "name": "browser_cache_accessible", + "status": "pass", + "message": "browser cache accessible: /home/coder/.cache/ms-playwright", + "durationMs": 0 + }, + { + "name": "browser_launch", + "status": "pass", + "message": "chromium launches", + "durationMs": 108 + }, + { + "name": "ghostty_web_available", + "status": "pass", + "message": "WASM available", + "durationMs": 112 + }, + { + "name": "screenshot_viable", + "status": "pass", + "message": "viable", + "durationMs": 157 + }, + { + "name": "libghostty_vt_available", + "status": "pass", + "message": "@coder/libghostty-vt-node exposes createTerminal()", + "durationMs": 1 + } + ] + }, + "capabilities": [ + { + "name": "snapshot", + "status": "available", + "reason": "libghostty-vt semantic renderer available", + "detail": "@coder/libghostty-vt-node exposes createTerminal()" + }, + { + "name": "wait", + "status": "available", + "reason": "libghostty-vt semantic renderer available", + "detail": "@coder/libghostty-vt-node exposes createTerminal()" + }, + { + "name": "screenshot", + "status": "available", + "reason": "renderer smoke checks passed", + "detail": "playwright_available: available; browser_launch: chromium launches; ghostty_web_available: WASM available; screenshot_viable: viable" + }, + { + "name": "record-export-asciicast", + "status": "available", + "reason": "built-in capability", + "detail": "available without external renderer dependencies" + }, + { + "name": "record-export-webm", + "status": "available", + "reason": "browser-backed export dependencies available", + "detail": "playwright_available: available; browser_launch: chromium launches; ghostty_web_available: WASM available" + }, + { + "name": "dashboard", + "status": "available", + "reason": "libghostty-vt native module available", + "detail": "@coder/libghostty-vt-node exposes createTerminal()" + } + ] + } +} diff --git a/dogfood/20260616-default-semantic-renderer/expected-renderer.json b/dogfood/20260616-default-semantic-renderer/expected-renderer.json new file mode 100644 index 00000000..a0cb0bfe --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/expected-renderer.json @@ -0,0 +1,3 @@ +{ + "expectedSemanticRenderer": "libghostty-vt" +} diff --git a/dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot-artifact.json b/dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot-artifact.json new file mode 100644 index 00000000..a789b807 --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot-artifact.json @@ -0,0 +1,16 @@ +{ + "id": "01KV851TCV0Q3J1QS8KR93E6FS", + "kind": "snapshot", + "filename": "snapshot-6-text.json", + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "capturedAtSeq": 6, + "createdAt": "2026-06-16T12:03:44.156Z", + "metadata": { + "format": "text", + "rendererBackend": "ghostty-web", + "cols": 80, + "rows": 24, + "cursorRow": 5, + "cursorCol": 7 + } +} diff --git a/dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot.json b/dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot.json new file mode 100644 index 00000000..1b989b3c --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot.json @@ -0,0 +1,16 @@ +{ + "ok": true, + "command": "snapshot", + "timestamp": "2026-06-16T12:03:44.160Z", + "result": { + "format": "text", + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "capturedAtSeq": 6, + "cols": 80, + "rows": 24, + "cursorRow": 5, + "cursorCol": 7, + "text": "READY> (node:2465832) [DEP0205] DeprecationWarning: `module.register()` is depre\ncated. Use `module.registerHooks()` instead.\n(Use `node --trace-deprecation ...` to show where the warning was created)\nDefault semantic renderer proof\nECHO: Default semantic renderer proof\nREADY>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "screenHash": "0feb80f00d88c52b4a000f6b1f718a63c6136bf8c9c2d90cc85c123cdf36f229" + } +} diff --git a/dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-screenshot.json b/dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-screenshot.json new file mode 100644 index 00000000..8a940b08 --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-screenshot.json @@ -0,0 +1,20 @@ +{ + "ok": true, + "command": "screenshot", + "timestamp": "2026-06-16T12:03:46.105Z", + "result": { + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "capturedAtSeq": 6, + "profileName": "reference-dark", + "cols": 80, + "rows": 24, + "artifactPath": "/tmp/agent-tty-default-renderer.w26eUX/sessions/01KV851DZH93PX9FQA3C199TSS/artifacts/screenshot-6-reference-dark.png", + "pngSizeBytes": 13319, + "cursorVisible": false, + "rendererBackend": "ghostty-web", + "pixelWidth": 640, + "pixelHeight": 384, + "sha256": "5864fb7aed3726a2a88ca79a7ce5db53159c35fcd0ca7982b3e15c367f173af7", + "renderProfileHash": "8ffed6af301ec7c0e6b69599c3be0d1d12096f9fcdfc59d0bbb4cc474d64c53d" + } +} diff --git a/dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-webm.json b/dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-webm.json new file mode 100644 index 00000000..72eecce4 --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-webm.json @@ -0,0 +1,24 @@ +{ + "ok": true, + "command": "record export", + "timestamp": "2026-06-16T12:04:00.110Z", + "result": { + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "format": "webm", + "artifactPath": "/tmp/agent-tty-default-renderer.w26eUX/sessions/01KV851DZH93PX9FQA3C199TSS/artifacts/recording-14-webm.webm", + "bytes": 49695, + "sha256": "26feddb53310eaf020503b6f75e73b96d731995d6fa104aa046601c3a94d0328", + "capturedAtSeq": 14, + "durationMs": 16177, + "metadata": { + "width": 80, + "height": 24, + "profileName": "reference-dark", + "renderProfileHash": "8ffed6af301ec7c0e6b69599c3be0d1d12096f9fcdfc59d0bbb4cc474d64c53d", + "timingMode": "accelerated", + "rendererBackend": "ghostty-web", + "outputEventCount": 10, + "resizeEventCount": 0 + } + } +} diff --git a/dogfood/20260616-default-semantic-renderer/inspect.json b/dogfood/20260616-default-semantic-renderer/inspect.json new file mode 100644 index 00000000..ad6c7153 --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/inspect.json @@ -0,0 +1,50 @@ +{ + "ok": true, + "command": "inspect", + "timestamp": "2026-06-16T12:04:02.980Z", + "result": { + "session": { + "version": 1, + "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "createdAt": "2026-06-16T12:03:31.444Z", + "updatedAt": "2026-06-16T12:03:48.740Z", + "status": "exited", + "command": ["npx", "tsx", "test/fixtures/apps/hello-prompt/main.ts"], + "cwd": "/home/coder/.mux/src/agent-terminal/vt-impl-5ds6", + "name": "default-semantic-renderer", + "shell": "/bin/bash", + "term": "xterm-256color", + "cols": 80, + "rows": 24, + "creationCols": 80, + "creationRows": 24, + "hostPid": 2465597, + "childPid": 2465610, + "exitCode": 0, + "exitSignal": null + }, + "eventCount": 15, + "uptime": 17296, + "lastEventSeq": 14, + "terminationCategory": "clean-exit", + "artifacts": { + "total": 7, + "byKind": { + "snapshot": 2, + "screenshot": 2, + "video": 2, + "recording": 1 + }, + "missingCount": 0, + "health": "healthy" + }, + "usedOfflineReplay": false, + "rendererRuntime": { + "backend": "ghostty-web", + "mode": "offline-replay", + "status": "fallback", + "reason": "session-not-running" + }, + "eventLogBytes": 1613 + } +} diff --git a/dogfood/20260616-default-semantic-renderer/recordings/default.cast b/dogfood/20260616-default-semantic-renderer/recordings/default.cast new file mode 100644 index 00000000..48498819 --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/recordings/default.cast @@ -0,0 +1,11 @@ +{"version":2,"width":80,"height":24,"timestamp":1781611412,"title":"01KV851DZH93PX9FQA3C199TSS","sessionId":"01KV851DZH93PX9FQA3C199TSS","env":{"TERM":"xterm-256color"},"toolVersion":"0.4.3"} +[0,"o","READY> "] +[0.001,"o","(node:2465832) [DEP0205] DeprecationWarning: `module.register()` is deprecated. Use `module.registerHooks()` instead.\r\n(Use `node --trace-deprecation ...` to show where the warning was created)\r\n"] +[2.492,"o","Default semantic renderer proof"] +[3.655,"o","\r\n"] +[3.655,"o","ECHO: Default semantic renderer proof\r\nREADY> "] +[14.993,"o","exit"] +[16.147,"o","\r\n"] +[16.147,"o","BYE\r\n"] +[16.166,"o","\\"] +[16.167,"o","\u001b[1G\u001b[0K"] diff --git a/dogfood/20260616-default-semantic-renderer/screenshots/default-screenshot.png b/dogfood/20260616-default-semantic-renderer/screenshots/default-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..7627a36802a553b06150f6632c2b0dc77dd823c1 GIT binary patch literal 13319 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 O;D)KywW_Ok{`^0}Vh;8I literal 0 HcmV?d00001 diff --git a/dogfood/20260616-default-semantic-renderer/screenshots/explicit-libghostty-vt-screenshot.png b/dogfood/20260616-default-semantic-renderer/screenshots/explicit-libghostty-vt-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..7627a36802a553b06150f6632c2b0dc77dd823c1 GIT binary patch literal 13319 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 O;D)KywW_Ok{`^0}Vh;8I literal 0 HcmV?d00001 diff --git a/dogfood/20260616-default-semantic-renderer/version.json b/dogfood/20260616-default-semantic-renderer/version.json new file mode 100644 index 00000000..9c89a5a9 --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/version.json @@ -0,0 +1,41 @@ +{ + "ok": true, + "command": "version", + "timestamp": "2026-06-16T12:03:28.301Z", + "result": { + "cliVersion": "0.4.3", + "protocolVersion": "0.1.0", + "rendererBackends": ["ghostty-web", "libghostty-vt"], + "runtime": { + "node": "v26.2.0", + "platform": "linux", + "arch": "x64" + }, + "capabilities": [ + { + "name": "snapshot", + "status": "available" + }, + { + "name": "wait", + "status": "available" + }, + { + "name": "screenshot", + "status": "available" + }, + { + "name": "record-export-asciicast", + "status": "available" + }, + { + "name": "record-export-webm", + "status": "available" + }, + { + "name": "dashboard", + "status": "available" + } + ] + } +} diff --git a/dogfood/20260616-default-semantic-renderer/videos/default-webm.webm b/dogfood/20260616-default-semantic-renderer/videos/default-webm.webm new file mode 100644 index 0000000000000000000000000000000000000000..8e301290f5d4a61c09e5fe90d84e12bdb56e520e GIT binary patch literal 49225 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 literal 0 HcmV?d00001 diff --git a/dogfood/20260616-default-semantic-renderer/videos/explicit-libghostty-vt-webm.webm b/dogfood/20260616-default-semantic-renderer/videos/explicit-libghostty-vt-webm.webm new file mode 100644 index 0000000000000000000000000000000000000000..a44f55e321c4a87d21f04e9a7cbd078f03b0a4df GIT binary patch literal 49695 zcmeFZ1yrTGmM*$*cPO9=cXxMpcXxM}LKp57P(b0Xg+t*E1r)A@ySuxr$KLzg?sNL} z=rQ`9?swl9ee-99jgO?VL&jE`}f}!5K`nC2rCj4;%RPTFA@#} z7YPM|RCMbiw;2hPjdeq&L5?!p_#7@MiCK`4~&sB_ORe!W_W*?m^6j| zAt)9ZAyljNXBqw|{CL&h$|gP!1ativQKTjWK!q^^W&!~uKQe>e9n2ztfUr;vLk%Ua zU?3Rjc5n!|i4I^Rr6vgcp*08`Cmjf`UK63-^!}$A2>u5GPp+;xrrm*-Luuj#;o=IS zielm_V&VT#2v^rR)9z@~f6dNvDEIFZC%sp|^t}SCe^h|T&f3Jo$;)ymPrM*POkG7- zO4GoP* z3yFXL2S9&-3Z`skLm+!LSXt!m`xiTevGNfr(XZOUE_3tb|JwhH!2eGXXa>B!4c< zJWyA|8B#Txl^+TgfdDGN6f6J%%?N-4z;V1m#J)iXVE;yVg1FQEaC@dDVX4VI?wYy( z?>mObXa8zWgx;z~=1EfMX03b(0R@9VfD#}9=3hwf-+;G6g{OizJI;O9?*Coq>^E%0 z-Hy|H zz^Vc_5fBP(<9_Mo$089t`KN?7#ghS^%i?{~-I0f4-?6zG+?#z3{!aRw@uo z2!Mw6UVSG34&vF+Wz(lGU@rjh`-uN0;QvFi5Ohh3KVCSU(*p~3fM8YtXZZJmDg6U5 zMhMOS+yiZ7eXiXV<6?K|1t^#Uf_(whzV}^Ju7AkoKk9S;ug+`R2j0N8;alXmuQX*? zMArx?XaIsW1JEGf$x{72*@G#xzc(n81{7zb>uU%2p1J(L|NLs>udn{D*S_=Ozx_%Z z0NYHl{5VDONjU;gFa`vN{Zne7{;#jV+us@JKUIU*X30C|J5t1BK!IH^5S$5cnFauR zr$;cl0~36Q_9A(4eh+%vZwYsQ4SrdFW<09CK>>lz-h5wB?i~ltR9d26ICo&W8MnIn zyK?=R1B^hRt&tZ?UIxQA)t8bQ!X3d;!7k8ekmMuiYU0pvuuDlW_zm;|d4qDP*sxwh zxEb&>AP%Gs5`N`>0+U%Nm7uLRD5Q;Pc^WKKzsLFxOaoiL)$W-yO1nT+*ss;{h< z;rEhr+0U=Nub#Iv_w_I9JNqq!N&b9Z2Rjp|IT34{Aj)T$>wtlr{jQGn$S&o#?{~4! z!Y6_?UBg|JpzOCkzo55IFQ1;mch+we*Fe3iL|*WLgueun0z%$y-zeX5o)xnvlAhjyd;?($ID$-Hn_e{UCr$(2O#%RHI5%BMw#n2M4SCg^KlP`dee`F5=ZW|xK+xtq{T9nQ_cQ8Zjq^v3jd(9 z#<56j8)oLL(psifW!<(EOZ2lB!WPdn{5F*p*fR~bfRX2vNvm3@rbS9sHJKa2#??V} ze92<+hpraWIpGHEnjY3Dzj3wu8tY4%uR1U;N)UJV&{@+#cQ_PIdf;P9U{Ct|rP^D^ zogfWAX%Ai$Q{TAJA0H9cdi#6)zrN+WbZC0pzM}_oy6^=dNr8M#|Gj8S_4>z5lfC^z z2iW}{ok|I2PH6y?xn1Vj#ami{pin1io>D(RTEAS~34H>;r7hbv^XlZl4#nZDMYQ}^ z1;tQSbDOp9%xny1Ll_zqw*hzPr=aClRp8_A894}+Km6}Iw;P6Fm zV+0f`gRo$fHfPbGpus=PaZJy|zpUmT)Q&|2+EjCaZ%Zwyw)jF5&iy^*XGkq5KfNtF zL(>9Va{?K8&-$|m7yIh zPcUCen=wbjliL;6U?T5&%N{&j;LQkfyz*k^I|DIfT|Pub)NuJw>p>0(;5L;B(MP0q z=Lf-45&ai9aK(Vv*;TRL&BYH

o$N=6p}SdbijG6q*^nLO*7nRBr$(@;d$yg}P{j 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

(D|gJ1V*}XY$NlP$mH4)s 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 { @@ -169,7 +169,7 @@ export async function runScreenshotCommand( 'screenshot', { profile, - rendererName: options.context.rendererDefault, + rendererName: options.context.rendererVisualDefault, ...(showCursor === undefined ? {} : { showCursor }), }, ); @@ -187,7 +187,7 @@ export async function runScreenshotCommand( result = await runOfflineScreenshot( sessionDirectory, - options.context.rendererDefault, + options.context.rendererVisualDefault, profile, showCursor, ); @@ -195,7 +195,7 @@ export async function runScreenshotCommand( } else { result = await runOfflineScreenshot( sessionDirectory, - options.context.rendererDefault, + options.context.rendererVisualDefault, profile, showCursor, ); diff --git a/src/cli/commands/version.ts b/src/cli/commands/version.ts index 952fe426..30d0e68e 100644 --- a/src/cli/commands/version.ts +++ b/src/cli/commands/version.ts @@ -4,6 +4,7 @@ import { emitSuccess } from '../output.js'; import type { CapabilityEntry } from '../../renderer/capabilities.js'; import { discoverCapabilities } from '../../renderer/capabilities.js'; +import { RendererNameSchema } from '../../renderer/names.js'; import { loadPackageMetadata } from '../../util/packageMetadata.js'; const COMMAND_NAME = 'version'; @@ -43,7 +44,7 @@ export async function buildVersionResult(options?: { return { cliVersion: packageMetadata.version, protocolVersion: PROTOCOL_VERSION, - rendererBackends: ['ghostty-web'], + rendererBackends: [...RendererNameSchema.options], runtime: { node: process.version, platform: process.platform, diff --git a/src/cli/context.ts b/src/cli/context.ts index 8b37c41d..72abbc67 100644 --- a/src/cli/context.ts +++ b/src/cli/context.ts @@ -8,9 +8,16 @@ import { loadConfigFile, type ConfigFile } from '../config/resolveConfig.js'; import { ERROR_CODES, makeCliError } from '../protocol/errors.js'; import { DEFAULT_RENDERER_NAME, + DEFAULT_SEMANTIC_RENDERER_NAME, + DEFAULT_VISUAL_RENDERER_NAME, + RendererNameSchema, resolveRendererName, type RendererName, } from '../renderer/names.js'; +import { + probeLibghosttyVt, + type LibghosttyVtProbe, +} from '../renderer/readiness.js'; import { resolveHome } from '../storage/home.js'; import { invariant } from '../util/assert.js'; import { @@ -41,10 +48,22 @@ export interface CommandContext { readonly logLevel: LogLevel; readonly logger: ReturnType; readonly profileDefault: string | undefined; + /** Default renderer for semantic operations: snapshot, render waits, hashes. */ readonly rendererDefault: RendererName; + /** Default renderer for visual artifacts: PNG screenshots and WebM exports. */ + readonly rendererVisualDefault: RendererName; readonly configFile: ConfigFile | null; } +interface RendererDefaultResolutionDeps { + probeLibghosttyVt?: () => Promise; +} + +interface RendererDefaults { + readonly semantic: RendererName; + readonly visual: RendererName; +} + interface CommandWithContext extends Command { [COMMAND_CONTEXT_SYMBOL]?: CommandContext; } @@ -108,6 +127,61 @@ export function resolveRendererDefault(raw?: string): RendererName { } } +let automaticSemanticRendererName: Promise | undefined; + +async function resolveAutomaticSemanticRenderer( + deps: RendererDefaultResolutionDeps, +): Promise { + const probe = deps.probeLibghosttyVt ?? probeLibghosttyVt; + const resolveFromProbe = async (): Promise => { + try { + const probeResult = await probe(); + return probeResult.available + ? DEFAULT_SEMANTIC_RENDERER_NAME + : DEFAULT_RENDERER_NAME; + } catch { + return DEFAULT_RENDERER_NAME; + } + }; + + if (deps.probeLibghosttyVt !== undefined) { + return resolveFromProbe(); + } + + automaticSemanticRendererName ??= resolveFromProbe(); + return automaticSemanticRendererName; +} + +export function clearRendererDefaultProbeCacheForTests(): void { + automaticSemanticRendererName = undefined; +} + +async function resolveRendererDefaults( + configuredRenderer: string | undefined, + deps: RendererDefaultResolutionDeps, +): Promise { + if (configuredRenderer !== undefined) { + const renderer = resolveRendererDefault(configuredRenderer); + invariant( + RendererNameSchema.safeParse(renderer).success, + 'configured renderer default must be a valid renderer name', + ); + return { semantic: renderer, visual: renderer }; + } + + const semantic = await resolveAutomaticSemanticRenderer(deps); + const visual = DEFAULT_VISUAL_RENDERER_NAME; + invariant( + RendererNameSchema.safeParse(semantic).success, + 'semantic renderer default must be a valid renderer name', + ); + invariant( + RendererNameSchema.safeParse(visual).success, + 'visual renderer default must be a valid renderer name', + ); + return { semantic, visual }; +} + export function resolveLogLevel(raw?: string): LogLevel { try { return resolveLoggerLevel(raw); @@ -125,6 +199,7 @@ export function resolveLogLevel(raw?: string): LogLevel { export async function resolveCommandContext( options: GlobalCliOptions, env: NodeJS.ProcessEnv = process.env, + deps: RendererDefaultResolutionDeps = {}, ): Promise { const configuredHome = options.home ?? env.AGENT_TTY_HOME; const explicitHome = configuredHome !== undefined; @@ -145,11 +220,9 @@ export async function resolveCommandContext( options.profile ?? env.AGENT_TTY_PROFILE ?? configFile?.defaultProfile; - const rendererDefault = resolveRendererDefault( - options.renderer ?? - env.AGENT_TTY_RENDERER ?? - configFile?.defaultRenderer ?? - DEFAULT_RENDERER_NAME, + const rendererDefaults = await resolveRendererDefaults( + options.renderer ?? env.AGENT_TTY_RENDERER ?? configFile?.defaultRenderer, + deps, ); return Object.freeze({ @@ -160,7 +233,8 @@ export async function resolveCommandContext( logLevel, logger, profileDefault, - rendererDefault, + rendererDefault: rendererDefaults.semantic, + rendererVisualDefault: rendererDefaults.visual, configFile, }); } diff --git a/src/cli/main.ts b/src/cli/main.ts index 02fd2ec2..1c299b45 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -99,6 +99,7 @@ function wrapAction( context.logger.debug(`starting ${commandName} command`, { logLevel: context.logLevel, renderer: context.rendererDefault, + visualRenderer: context.rendererVisualDefault, }); const args = rawArgs.slice(0, -1) as Args; await fn(...([...args, context] as [...Args, CommandContext])); @@ -138,7 +139,7 @@ async function main(): Promise { .option('--profile ', 'Default render profile name') .option( '--renderer ', - 'Renderer backend (ghostty-web or libghostty-vt)', + 'Renderer backend override. Defaults vary by command: semantic actions prefer libghostty-vt when available; visual artifacts default to ghostty-web.', ); program.hook('preAction', async (_thisCommand, actionCommand) => { @@ -160,6 +161,8 @@ async function main(): Promise { command: actionCommand.name(), home: context.home, logLevel: context.logLevel, + renderer: context.rendererDefault, + visualRenderer: context.rendererVisualDefault, }); }); diff --git a/src/host/hostMain.ts b/src/host/hostMain.ts index 2c322ab2..dba6d08d 100644 --- a/src/host/hostMain.ts +++ b/src/host/hostMain.ts @@ -149,6 +149,7 @@ export async function runHost(sessionId: string): Promise { 'session manifest idleTimeoutMs must be a non-negative integer', ); + const hostDefaultRendererName = resolveHostRendererName(undefined); const state = new SessionState(manifest); invariant( Number.isInteger(process.pid) && process.pid > 0, @@ -435,6 +436,8 @@ export async function runHost(sessionId: string): Promise { // reads, producing a result that does not correspond to any real // point in time. const sessionSnapshot = state.snapshot(); + const rendererBackend = + rendererManager.getCurrentRendererName() ?? hostDefaultRendererName; const rendererProfile = rendererManager.getCurrentProfileName(); const rendererBooted = rendererManager.isBooted(); const rendererBootInFlight = rendererManager.isBootInFlight(); @@ -446,6 +449,7 @@ export async function runHost(sessionId: string): Promise { ? { cliVersion: packageMetadata.version } : {}), rpcSocketPath: sPath, + rendererBackend, ...(rendererProfile !== null ? { rendererProfile } : {}), rendererBooted, rendererBootInFlight, diff --git a/src/host/renderer.ts b/src/host/renderer.ts index 582feafd..8fe0dac3 100644 --- a/src/host/renderer.ts +++ b/src/host/renderer.ts @@ -146,6 +146,10 @@ export class HostRendererManager { return this.bootPromise !== null; } + getCurrentRendererName(): string | null { + return this.currentBackend?.rendererBackend ?? null; + } + getCurrentProfileName(): string | null { return this.currentProfileName; } diff --git a/src/protocol/messages.ts b/src/protocol/messages.ts index fe429e32..efad2a9a 100644 --- a/src/protocol/messages.ts +++ b/src/protocol/messages.ts @@ -115,6 +115,7 @@ export const HostInspectResultSchema = z // Populated only when the host has reached the inspect handler. An // older host or one whose renderer has never bootstrapped omits // these and the CLI surfaces them as absent on `rendererRuntime`. + rendererBackend: z.string().min(1).optional(), rendererProfile: z.string().min(1).optional(), rendererBooted: z.boolean().optional(), rendererBootInFlight: z.boolean().optional(), diff --git a/src/renderer/capabilities.ts b/src/renderer/capabilities.ts index 98c3363b..a9bcf3fe 100644 --- a/src/renderer/capabilities.ts +++ b/src/renderer/capabilities.ts @@ -84,9 +84,9 @@ const CAPABILITY_NAMES: ReadonlyArray = Object.freeze([ 'record-export-webm', 'dashboard', ]); +const SEMANTIC_RENDER_CAPABILITY_NAMES: ReadonlyArray<'snapshot' | 'wait'> = + Object.freeze(['snapshot', 'wait']); const BUILTIN_CAPABILITY_NAMES: ReadonlyArray = Object.freeze([ - 'snapshot', - 'wait', 'record-export-asciicast', ]); @@ -165,9 +165,9 @@ async function probePlaywrightAvailability( } /** - * Built-in capabilities (snapshot, wait, record-export-asciicast) are always - * reported as 'available' because they depend only on the event log and - * built-in text processing; no external renderer or browser is needed. + * Built-in capabilities are always reported as 'available' because they depend + * only on the event log and built-in text processing; no external renderer or + * browser is needed. * * This reflects runtime feature availability, not guaranteed success for any * particular session. Corrupted session data or an invalid event log will fail @@ -216,6 +216,138 @@ function buildUnknownCapability(name: CapabilityName): CapabilityEntry { }; } +function buildSemanticUnavailableDetail( + libghosttyVtProbe: LibghosttyVtProbe, + playwrightProbe: PlaywrightProbeResult, +): string | undefined { + const details = [libghosttyVtProbe.detail, playwrightProbe.detail].filter( + (detail): detail is string => detail !== undefined && detail.length > 0, + ); + return details.length === 0 ? undefined : details.join('; '); +} + +async function buildSemanticRenderCapability( + name: 'snapshot' | 'wait', + mode: DiscoveryMode, + deps: CapabilityDiscoveryDependencies, +): Promise { + if (mode === 'full' && deps.rendererChecks !== undefined) { + return buildFullSemanticRenderCapabilityFromChecks( + name, + deps.rendererChecks, + ); + } + + const probeLibghostty = deps.probeLibghosttyVt ?? probeLibghosttyVt; + const probePlaywright = deps.probePlaywright ?? probePlaywrightAvailability; + const [libghosttyVtProbe, playwrightProbe] = await Promise.all([ + probeLibghostty(), + probePlaywright(mode), + ]); + + if (libghosttyVtProbe.available) { + return mode === 'full' + ? { + name, + status: 'available', + reason: libghosttyVtProbe.reason, + detail: libghosttyVtProbe.detail, + } + : { name, status: 'available' }; + } + + if (playwrightProbe.available) { + return mode === 'full' + ? { + name, + status: 'available', + reason: 'ghostty-web semantic fallback available', + detail: playwrightProbe.detail, + } + : { name, status: 'available' }; + } + + return { + name, + status: 'unavailable', + reason: 'semantic renderer unavailable', + detail: buildSemanticUnavailableDetail(libghosttyVtProbe, playwrightProbe), + }; +} + +function buildFullSemanticRenderCapabilityFromChecks( + name: 'snapshot' | 'wait', + checks: ReadonlyArray, +): CapabilityEntry { + const libghosttyVtCheck = findRendererCheck( + checks, + 'libghostty_vt_available', + ); + const playwrightCheck = findRendererCheck(checks, 'playwright_available'); + const browserLaunchCheck = findRendererCheck(checks, 'browser_launch'); + const ghosttyWebCheck = findRendererCheck(checks, 'ghostty_web_available'); + + if (libghosttyVtCheck === undefined) { + return buildUnknownCapability(name); + } + + if (libghosttyVtCheck.status === 'pass') { + return { + name, + status: 'available', + reason: 'libghostty-vt semantic renderer available', + detail: libghosttyVtCheck.message, + }; + } + + if ( + playwrightCheck === undefined || + browserLaunchCheck === undefined || + ghosttyWebCheck === undefined + ) { + return buildUnknownCapability(name); + } + + if (playwrightCheck.status === 'fail') { + return { + name, + status: 'unavailable', + reason: 'semantic renderer unavailable', + detail: `libghostty-vt: ${libghosttyVtCheck.message}; playwright: ${playwrightCheck.message}`, + }; + } + + if (ghosttyWebCheck.status === 'fail') { + return { + name, + status: 'unavailable', + reason: 'semantic renderer unavailable', + detail: `libghostty-vt: ${libghosttyVtCheck.message}; ghostty-web: ${ghosttyWebCheck.message}`, + }; + } + + if (browserLaunchCheck.status === 'fail') { + return { + name, + status: 'unavailable', + reason: 'semantic renderer unavailable', + detail: `libghostty-vt: ${libghosttyVtCheck.message}; browser: ${browserLaunchCheck.message}`, + }; + } + + return { + name, + status: 'available', + reason: 'ghostty-web semantic fallback available', + detail: buildAvailableDetail([ + libghosttyVtCheck, + playwrightCheck, + browserLaunchCheck, + ghosttyWebCheck, + ]), + }; +} + function buildFullScreenshotCapabilityFromChecks( checks: ReadonlyArray, ): CapabilityEntry { @@ -437,16 +569,45 @@ export async function discoverCapabilities( deps: CapabilityDiscoveryDependencies = {}, ): Promise { const capabilities: CapabilityEntry[] = []; + const baseProbePlaywright = + deps.probePlaywright ?? probePlaywrightAvailability; + const baseProbeLibghosttyVt = deps.probeLibghosttyVt ?? probeLibghosttyVt; + let cachedPlaywrightProbe: Promise | undefined; + let cachedLibghosttyVtProbe: Promise | undefined; + const cachedDeps: CapabilityDiscoveryDependencies = { + ...deps, + probePlaywright: (requestedMode) => { + assert.equal( + requestedMode, + mode, + 'capability discovery probe mode must stay consistent', + ); + cachedPlaywrightProbe ??= baseProbePlaywright(requestedMode); + return cachedPlaywrightProbe; + }, + probeLibghosttyVt: () => { + cachedLibghosttyVtProbe ??= baseProbeLibghosttyVt(); + return cachedLibghosttyVtProbe; + }, + }; + + for (const name of SEMANTIC_RENDER_CAPABILITY_NAMES) { + capabilities.push( + await buildSemanticRenderCapability(name, mode, cachedDeps), + ); + } for (const name of BUILTIN_CAPABILITY_NAMES) { capabilities.push(buildBuiltinCapability(name, mode)); } - capabilities.push(await buildPlaywrightCapability('screenshot', mode, deps)); capabilities.push( - await buildPlaywrightCapability('record-export-webm', mode, deps), + await buildPlaywrightCapability('screenshot', mode, cachedDeps), + ); + capabilities.push( + await buildPlaywrightCapability('record-export-webm', mode, cachedDeps), ); - capabilities.push(await discoverDashboardCapability(mode, deps)); + capabilities.push(await discoverDashboardCapability(mode, cachedDeps)); const sortedCapabilities: CapabilityEntry[] = []; for (const name of CAPABILITY_NAMES) { diff --git a/src/renderer/index.ts b/src/renderer/index.ts index dd9439ec..394ecbbf 100644 --- a/src/renderer/index.ts +++ b/src/renderer/index.ts @@ -17,6 +17,8 @@ export { VisibleLineSchema } from '../protocol/schemas.js'; export type { RendererBackend, SnapshotOptions } from './backend.js'; export { DEFAULT_RENDERER_NAME, + DEFAULT_SEMANTIC_RENDERER_NAME, + DEFAULT_VISUAL_RENDERER_NAME, RendererNameSchema, resolveRendererName, } from './names.js'; diff --git a/src/renderer/libghosttyVt/backend.ts b/src/renderer/libghosttyVt/backend.ts index 5f68a9ff..d8c7255f 100644 --- a/src/renderer/libghosttyVt/backend.ts +++ b/src/renderer/libghosttyVt/backend.ts @@ -33,6 +33,8 @@ export interface LibghosttyVtNativeModule { getNativeInfo?: () => NativeInfo; } +const DEFAULT_SCROLLBACK_LIMIT = 10_000; + export interface LibghosttyVtBackendOptions { initialCols?: number; initialRows?: number; @@ -369,7 +371,7 @@ export class LibghosttyVtBackend implements RendererBackend { >; private readonly logger: Logger; private readonly profile: RenderProfileConfig; - private readonly scrollbackLimit: number | undefined; + private readonly scrollbackLimit: number; private readonly sessionId: string; private bootPromise: Promise | null = null; @@ -411,7 +413,7 @@ export class LibghosttyVtBackend implements RendererBackend { this.initialRows = initialRows; this.currentCols = initialCols; this.currentRows = initialRows; - this.scrollbackLimit = options.scrollbackLimit; + this.scrollbackLimit = options.scrollbackLimit ?? DEFAULT_SCROLLBACK_LIMIT; this.loadNative = options.loadNative ?? (() => @@ -659,9 +661,7 @@ export class LibghosttyVtBackend implements RendererBackend { this.terminal = native.createTerminal({ cols: this.initialCols, rows: this.initialRows, - ...(this.scrollbackLimit === undefined - ? {} - : { scrollbackLimit: this.scrollbackLimit }), + scrollbackLimit: this.scrollbackLimit, }); this.assertTerminalShape(this.terminal); this.currentCols = this.initialCols; diff --git a/src/renderer/names.ts b/src/renderer/names.ts index 7e0f58fd..af4dcd2c 100644 --- a/src/renderer/names.ts +++ b/src/renderer/names.ts @@ -3,7 +3,11 @@ import { z } from 'zod'; export const RendererNameSchema = z.enum(['ghostty-web', 'libghostty-vt']); export type RendererName = z.infer; +// Legacy/safe single-renderer fallback for internal paths that cannot yet +// distinguish semantic from visual defaults. export const DEFAULT_RENDERER_NAME: RendererName = 'ghostty-web'; +export const DEFAULT_SEMANTIC_RENDERER_NAME: RendererName = 'libghostty-vt'; +export const DEFAULT_VISUAL_RENDERER_NAME: RendererName = 'ghostty-web'; export function resolveRendererName(input: string | undefined): RendererName { const candidate = input ?? DEFAULT_RENDERER_NAME; diff --git a/test/e2e/hello-prompt.test.ts b/test/e2e/hello-prompt.test.ts index 7c347253..9a2d865c 100644 --- a/test/e2e/hello-prompt.test.ts +++ b/test/e2e/hello-prompt.test.ts @@ -1,5 +1,6 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { probeLibghosttyVt } from '../../src/renderer/readiness.js'; import { cleanupHome, createIsolatedHome, @@ -59,6 +60,9 @@ describe('hello-prompt e2e', { timeout: 30_000 }, () => { }); it('full interaction flow', async () => { + const expectedRenderer = (await probeLibghosttyVt()).available + ? 'libghostty-vt' + : 'ghostty-web'; const env = testEnv(testHome); const createEnvelope = runCliJson>( ['create', '--', ...fixtureCommand('hello-prompt')], @@ -142,7 +146,7 @@ describe('hello-prompt e2e', { timeout: 30_000 }, () => { expect(inspectRunning.result.session.exitCode).toBeNull(); expect(inspectRunning.result.rendererRuntime).toEqual( expect.objectContaining({ - backend: 'ghostty-web', + backend: expectedRenderer, mode: 'live-host', status: 'healthy', }), diff --git a/test/integration/backend-selection.test.ts b/test/integration/backend-selection.test.ts index ee3d16fa..5619ff6a 100644 --- a/test/integration/backend-selection.test.ts +++ b/test/integration/backend-selection.test.ts @@ -10,6 +10,9 @@ import { destroySession, runCli, } from '../helpers.js'; +import { probeLibghosttyVt } from '../../src/renderer/readiness.js'; +import { readArtifactManifest } from '../../src/storage/artifactManifest.js'; +import { sessionDir } from '../../src/storage/sessionPaths.js'; interface FailureEnvelope { ok: false; @@ -87,6 +90,40 @@ describe('backend selection integration', () => { }); }); + it('defaults semantic snapshot rendering to libghostty-vt when available', async () => { + const expectedRenderer = (await probeLibghosttyVt()).available + ? 'libghostty-vt' + : 'ghostty-web'; + sessionId = createSession(testHome, [ + '/bin/sh', + '-c', + 'printf default-renderer-ready; exec cat', + ]); + + const result = runCli( + ['snapshot', sessionId, '--format', 'structured', '--json'], + { AGENT_TTY_HOME: testHome }, + 60_000, + ); + + expect(result.status).toBe(0); + expect(result.stderr).toBe(''); + expect(JSON.parse(result.stdout)).toMatchObject({ + ok: true, + result: { sessionId }, + }); + await expect( + readArtifactManifest(sessionDir(testHome, sessionId)), + ).resolves.toMatchObject({ + artifacts: [ + { + kind: 'snapshot', + metadata: { rendererBackend: expectedRenderer }, + }, + ], + }); + }); + it('threads --renderer ghostty-web through live snapshot RPC paths', () => { sessionId = createSession(testHome, [ '/bin/sh', diff --git a/test/integration/cli.test.ts b/test/integration/cli.test.ts index c97b5dd8..f8d8f43a 100644 --- a/test/integration/cli.test.ts +++ b/test/integration/cli.test.ts @@ -71,7 +71,10 @@ describe('CLI integration', () => { expect(parsed.ok).toBe(true); expect(parsed.command).toBe('version'); expect(parsed.result.cliVersion).toMatch(SEMVER_WITH_OPTIONAL_PRERELEASE); - expect(parsed.result.rendererBackends).toEqual(['ghostty-web']); + expect(parsed.result.rendererBackends).toEqual([ + 'ghostty-web', + 'libghostty-vt', + ]); }); it('lists bundled skills in human output', () => { diff --git a/test/integration/host-renderer-rpc.test.ts b/test/integration/host-renderer-rpc.test.ts index ce95fbd5..8e3cdd21 100644 --- a/test/integration/host-renderer-rpc.test.ts +++ b/test/integration/host-renderer-rpc.test.ts @@ -4,6 +4,7 @@ import { join } from 'node:path'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { probeLibghosttyVt } from '../../src/renderer/readiness.js'; import { sendRpc } from '../../src/host/rpcClient.js'; import type { ScreenshotResult, @@ -79,11 +80,16 @@ describe( let testHome = ''; let sessionId = ''; let rpcSocketPath = ''; + let expectedSemanticRenderer = 'ghostty-web'; + let sessDir = ''; beforeEach(async () => { // oxfmt-ignore testHome = await realpath(await mkdtemp(join(tmpdir(), 'agent-tty-host-renderer-'))); + expectedSemanticRenderer = (await probeLibghosttyVt()).available + ? 'libghostty-vt' + : 'ghostty-web'; sessionId = createSession(testHome, [ '/bin/sh', '-c', @@ -157,7 +163,7 @@ describe( rows: result.rows, cursorRow: result.cursorRow, cursorCol: result.cursorCol, - rendererBackend: 'ghostty-web', + rendererBackend: expectedSemanticRenderer, }), ); }); @@ -200,7 +206,7 @@ describe( rows: result.rows, cursorRow: result.cursorRow, cursorCol: result.cursorCol, - rendererBackend: 'ghostty-web', + rendererBackend: expectedSemanticRenderer, }), ); }); @@ -278,7 +284,7 @@ describe( rows: result.rows, cursorRow: result.cursorRow, cursorCol: result.cursorCol, - rendererBackend: 'ghostty-web', + rendererBackend: expectedSemanticRenderer, scrollbackLineCount: scrollbackLines.length, }), ); @@ -298,7 +304,7 @@ describe( rows: result.rows, cursorRow: result.cursorRow, cursorCol: result.cursorCol, - rendererBackend: 'ghostty-web', + rendererBackend: expectedSemanticRenderer, }), ); expect(manifest.artifacts[0]?.metadata).not.toHaveProperty( diff --git a/test/integration/lifecycle.test.ts b/test/integration/lifecycle.test.ts index ad23ede0..56005da6 100644 --- a/test/integration/lifecycle.test.ts +++ b/test/integration/lifecycle.test.ts @@ -11,6 +11,7 @@ import { join } from 'node:path'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { probeLibghosttyVt } from '../../src/renderer/readiness.js'; import { SessionRecordSchema } from '../../src/protocol/schemas.js'; import { cleanupHome, @@ -77,7 +78,7 @@ describe('lifecycle integration', { timeout: 30000 }, () => { await cleanupHome(testHome); }); - it('full lifecycle: create → list → inspect → destroy', () => { + it('full lifecycle: create → list → inspect → destroy', async () => { const createResult = runCli( ['create', '--json', '--', '/bin/sh', '-c', 'echo ready; sleep 30'], { AGENT_TTY_HOME: testHome }, @@ -112,6 +113,9 @@ describe('lifecycle integration', { timeout: 30000 }, () => { expect(listedSession?.name).toBeUndefined(); expect(listedSession?.pid).toBeTypeOf('number'); + const expectedRenderer = (await probeLibghosttyVt()).available + ? 'libghostty-vt' + : 'ghostty-web'; const inspectResult = runCli(['inspect', sessionId, '--json'], { AGENT_TTY_HOME: testHome, }); @@ -135,7 +139,7 @@ describe('lifecycle integration', { timeout: 30000 }, () => { expect(inspectEnvelope.result.session.childPid).toBeTypeOf('number'); expect(inspectEnvelope.result.rendererRuntime).toEqual( expect.objectContaining({ - backend: 'ghostty-web', + backend: expectedRenderer, mode: 'live-host', status: 'healthy', }), diff --git a/test/unit/cli/context.test.ts b/test/unit/cli/context.test.ts index 614df6eb..f901da7e 100644 --- a/test/unit/cli/context.test.ts +++ b/test/unit/cli/context.test.ts @@ -1,9 +1,11 @@ import { Command } from 'commander'; import type * as ResolveConfigModule from '../../../src/config/resolveConfig.js'; +import type * as RendererReadinessModule from '../../../src/renderer/readiness.js'; import { beforeEach, describe, expect, it, vi } from 'vitest'; const mocks = vi.hoisted(() => ({ loadConfigFile: vi.fn(), + probeLibghosttyVt: vi.fn(), })); vi.mock('../../../src/config/resolveConfig.js', async () => { @@ -16,7 +18,18 @@ vi.mock('../../../src/config/resolveConfig.js', async () => { }; }); +vi.mock('../../../src/renderer/readiness.js', async () => { + const actual = await vi.importActual( + '../../../src/renderer/readiness.js', + ); + return { + ...actual, + probeLibghosttyVt: mocks.probeLibghosttyVt, + }; +}); + import { + clearRendererDefaultProbeCacheForTests, getCommandContext, parseTimeoutMsOption, resolveCommandContext, @@ -33,7 +46,12 @@ const TEST_FLAG_HOME = '/tmp/from-flag'; describe('CLI context resolution', () => { beforeEach(() => { vi.clearAllMocks(); + clearRendererDefaultProbeCacheForTests(); mocks.loadConfigFile.mockResolvedValue(null); + mocks.probeLibghosttyVt.mockResolvedValue({ + available: false, + reason: 'libghostty-vt not installed', + }); }); it('prefers --home over AGENT_TTY_HOME', async () => { @@ -157,7 +175,7 @@ describe('CLI context resolution', () => { ).resolves.toMatchObject({ profileDefault: undefined }); }); - it('resolves rendererDefault from flag, env, config, and default precedence', async () => { + it('resolves explicit renderer defaults from flag, env, and config precedence', async () => { mocks.loadConfigFile.mockResolvedValue({ defaultRenderer: 'libghostty-vt', }); @@ -167,7 +185,10 @@ describe('CLI context resolution', () => { { home: TEST_FLAG_HOME, renderer: 'ghostty-web' }, {}, ), - ).resolves.toMatchObject({ rendererDefault: 'ghostty-web' }); + ).resolves.toMatchObject({ + rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', + }); await expect( resolveCommandContext( { home: TEST_FLAG_HOME }, @@ -176,15 +197,58 @@ describe('CLI context resolution', () => { AGENT_TTY_RENDERER: 'ghostty-web', }, ), - ).resolves.toMatchObject({ rendererDefault: 'ghostty-web' }); + ).resolves.toMatchObject({ + rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', + }); await expect( resolveCommandContext({ home: TEST_FLAG_HOME }, {}), - ).resolves.toMatchObject({ rendererDefault: 'libghostty-vt' }); + ).resolves.toMatchObject({ + rendererDefault: 'libghostty-vt', + rendererVisualDefault: 'libghostty-vt', + }); + }); + + it('prefers libghostty-vt for automatic semantic defaults when available', async () => { + mocks.probeLibghosttyVt.mockResolvedValue({ available: true }); + + await expect( + resolveCommandContext({ home: TEST_FLAG_HOME }, {}), + ).resolves.toMatchObject({ + rendererDefault: 'libghostty-vt', + rendererVisualDefault: 'ghostty-web', + }); + }); + + it('falls back to ghostty-web automatic semantic defaults when libghostty-vt is unavailable', async () => { + mocks.probeLibghosttyVt.mockResolvedValue({ available: false }); - mocks.loadConfigFile.mockResolvedValue(null); await expect( resolveCommandContext({ home: TEST_FLAG_HOME }, {}), - ).resolves.toMatchObject({ rendererDefault: 'ghostty-web' }); + ).resolves.toMatchObject({ + rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', + }); + }); + + it('falls back to ghostty-web automatic semantic defaults when probing fails', async () => { + mocks.probeLibghosttyVt.mockRejectedValue(new Error('probe failed')); + + await expect( + resolveCommandContext({ home: TEST_FLAG_HOME }, {}), + ).resolves.toMatchObject({ + rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', + }); + }); + + it('memoizes the automatic semantic renderer probe', async () => { + mocks.probeLibghosttyVt.mockResolvedValue({ available: true }); + + await resolveCommandContext({ home: TEST_FLAG_HOME }, {}); + await resolveCommandContext({ home: TEST_FLAG_HOME }, {}); + + expect(mocks.probeLibghosttyVt).toHaveBeenCalledTimes(1); }); it('rejects invalid renderer names', async () => { @@ -231,6 +295,7 @@ describe('CLI context resolution', () => { logger: createLogger('info', () => undefined), profileDefault: 'default-profile', rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', configFile: null, }); setCommandContext(command, cachedContext); diff --git a/test/unit/commands/create.test.ts b/test/unit/commands/create.test.ts index d5cb9683..4d69d462 100644 --- a/test/unit/commands/create.test.ts +++ b/test/unit/commands/create.test.ts @@ -56,6 +56,7 @@ describe('create command', () => { logger: createLogger('info', () => undefined), profileDefault: undefined, rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', explicitHome: false, configFile: null, } as const; diff --git a/test/unit/commands/doctor.test.ts b/test/unit/commands/doctor.test.ts index 3a79647f..893c9595 100644 --- a/test/unit/commands/doctor.test.ts +++ b/test/unit/commands/doctor.test.ts @@ -138,14 +138,12 @@ describe('doctor command', () => { 'record-export-webm', 'dashboard', ]); - expect(result.capabilities.find(({ name }) => name === 'snapshot')).toEqual( - { - name: 'snapshot', - status: 'available', - reason: 'built-in capability', - detail: 'available without external renderer dependencies', - }, - ); + expect( + result.capabilities.find(({ name }) => name === 'snapshot'), + ).toMatchObject({ + name: 'snapshot', + status: 'available', + }); expect( result.capabilities.find(({ name }) => name === 'screenshot'), ).toMatchObject({ diff --git a/test/unit/commands/gc.test.ts b/test/unit/commands/gc.test.ts index 66c9e497..5abd8c63 100644 --- a/test/unit/commands/gc.test.ts +++ b/test/unit/commands/gc.test.ts @@ -506,6 +506,7 @@ describe('runGcCommand (cross-Home sweep)', () => { logger: createLogger('info', () => undefined), profileDefault: undefined, rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', configFile: null, } as const; } diff --git a/test/unit/commands/inspect.test.ts b/test/unit/commands/inspect.test.ts index f300344a..6e406ec0 100644 --- a/test/unit/commands/inspect.test.ts +++ b/test/unit/commands/inspect.test.ts @@ -67,6 +67,7 @@ const TEST_CONTEXT = { logger: createLogger('info', () => undefined), profileDefault: undefined, rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', explicitHome: false, configFile: null, } as const; @@ -520,6 +521,7 @@ describe('inspect command', () => { session: liveSession, cliVersion: '0.2.1', rpcSocketPath: '/tmp/agent-tty/sessions/session-01/rpc.sock', + rendererBackend: 'libghostty-vt', rendererProfile: 'reference-dark', rendererBooted: true, rendererBootInFlight: false, @@ -551,7 +553,7 @@ describe('inspect command', () => { rpcSocketPath: '/tmp/agent-tty/sessions/session-01/rpc.sock', }); expect(emitted.result.rendererRuntime).toEqual({ - backend: 'ghostty-web', + backend: 'libghostty-vt', mode: 'live-host', status: 'healthy', profile: 'reference-dark', @@ -562,7 +564,7 @@ describe('inspect command', () => { expect.arrayContaining([ 'Host CLI Version: 0.2.1', 'RPC Socket: /tmp/agent-tty/sessions/session-01/rpc.sock', - 'Renderer: ghostty-web (live-host, healthy) [profile: reference-dark, booted: yes]', + 'Renderer: libghostty-vt (live-host, healthy) [profile: reference-dark, booted: yes]', ]), ); }); diff --git a/test/unit/commands/mark.test.ts b/test/unit/commands/mark.test.ts index 291285d3..9e6fb3cb 100644 --- a/test/unit/commands/mark.test.ts +++ b/test/unit/commands/mark.test.ts @@ -31,6 +31,7 @@ const TEST_CONTEXT = { logger: createLogger('info', () => undefined), profileDefault: undefined, rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', explicitHome: false, configFile: null, } as const; diff --git a/test/unit/commands/paste.test.ts b/test/unit/commands/paste.test.ts index da5f1297..3d5a7079 100644 --- a/test/unit/commands/paste.test.ts +++ b/test/unit/commands/paste.test.ts @@ -34,6 +34,7 @@ const TEST_CONTEXT = { logger: createLogger('info', () => undefined), profileDefault: undefined, rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', explicitHome: false, configFile: null, } as const; diff --git a/test/unit/commands/record-export.test.ts b/test/unit/commands/record-export.test.ts index 30481ff0..f80304d0 100644 --- a/test/unit/commands/record-export.test.ts +++ b/test/unit/commands/record-export.test.ts @@ -102,7 +102,8 @@ const TEST_CONTEXT = { logLevel: 'info', logger: createLogger('info', () => undefined), profileDefault: undefined, - rendererDefault: 'ghostty-web', + rendererDefault: 'libghostty-vt', + rendererVisualDefault: 'ghostty-web', explicitHome: false, configFile: null, } as const; diff --git a/test/unit/commands/resize.test.ts b/test/unit/commands/resize.test.ts index e1be4d7c..2139eb9f 100644 --- a/test/unit/commands/resize.test.ts +++ b/test/unit/commands/resize.test.ts @@ -31,6 +31,7 @@ const TEST_CONTEXT = { logger: createLogger('info', () => undefined), profileDefault: undefined, rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', explicitHome: false, configFile: null, } as const; diff --git a/test/unit/commands/run.test.ts b/test/unit/commands/run.test.ts index 3bc966a9..bee3bba3 100644 --- a/test/unit/commands/run.test.ts +++ b/test/unit/commands/run.test.ts @@ -35,6 +35,7 @@ const TEST_CONTEXT = { logger: createLogger('info', () => undefined), profileDefault: undefined, rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', explicitHome: false, configFile: null, } as const; diff --git a/test/unit/commands/screenshot.test.ts b/test/unit/commands/screenshot.test.ts index eadcb3cf..2db0b064 100644 --- a/test/unit/commands/screenshot.test.ts +++ b/test/unit/commands/screenshot.test.ts @@ -79,7 +79,8 @@ const TEST_CONTEXT = { logLevel: 'info', logger: createLogger('info', () => undefined), profileDefault: undefined, - rendererDefault: 'ghostty-web', + rendererDefault: 'libghostty-vt', + rendererVisualDefault: 'ghostty-web', explicitHome: false, configFile: null, } as const; diff --git a/test/unit/commands/send-keys.test.ts b/test/unit/commands/send-keys.test.ts index c9528732..a132315e 100644 --- a/test/unit/commands/send-keys.test.ts +++ b/test/unit/commands/send-keys.test.ts @@ -31,6 +31,7 @@ const TEST_CONTEXT = { logger: createLogger('info', () => undefined), profileDefault: undefined, rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', explicitHome: false, configFile: null, } as const; diff --git a/test/unit/commands/signal.test.ts b/test/unit/commands/signal.test.ts index 341b64be..b8414409 100644 --- a/test/unit/commands/signal.test.ts +++ b/test/unit/commands/signal.test.ts @@ -31,6 +31,7 @@ const TEST_CONTEXT = { logger: createLogger('info', () => undefined), profileDefault: undefined, rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', explicitHome: false, configFile: null, } as const; diff --git a/test/unit/commands/snapshot.test.ts b/test/unit/commands/snapshot.test.ts index 74289316..a8adbf22 100644 --- a/test/unit/commands/snapshot.test.ts +++ b/test/unit/commands/snapshot.test.ts @@ -73,6 +73,7 @@ const TEST_CONTEXT = { logger: createLogger('info', () => undefined), profileDefault: undefined, rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', explicitHome: false, configFile: null, } as const; diff --git a/test/unit/commands/type.test.ts b/test/unit/commands/type.test.ts index 39c56cf6..f66c48d0 100644 --- a/test/unit/commands/type.test.ts +++ b/test/unit/commands/type.test.ts @@ -34,6 +34,7 @@ const TEST_CONTEXT = { logger: createLogger('info', () => undefined), profileDefault: undefined, rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', explicitHome: false, configFile: null, } as const; diff --git a/test/unit/commands/version.test.ts b/test/unit/commands/version.test.ts index 422a1c3d..8fc3f01b 100644 --- a/test/unit/commands/version.test.ts +++ b/test/unit/commands/version.test.ts @@ -19,7 +19,7 @@ describe('version command', () => { expect(result.cliVersion).toMatch(SEMVER_WITH_OPTIONAL_PRERELEASE); expect(result.protocolVersion).toBe('0.1.0'); - expect(result.rendererBackends).toEqual(['ghostty-web']); + expect(result.rendererBackends).toEqual(['ghostty-web', 'libghostty-vt']); expect(result.runtime.node).toMatch(/^v\d+\.\d+\.\d+$/); expect('capabilities' in result).toBe(false); }); @@ -56,7 +56,7 @@ describe('version command', () => { expect(result.cliVersion).toMatch(SEMVER_WITH_OPTIONAL_PRERELEASE); expect(result.protocolVersion).toBe('0.1.0'); - expect(result.rendererBackends).toEqual(['ghostty-web']); + expect(result.rendererBackends).toEqual(['ghostty-web', 'libghostty-vt']); expect(result.runtime.node).toMatch(/^v\d+\.\d+\.\d+$/); expect(result.runtime.platform).toBe(process.platform); expect(result.runtime.arch).toBe(process.arch); diff --git a/test/unit/commands/wait.test.ts b/test/unit/commands/wait.test.ts index f9022bcc..d3953a2c 100644 --- a/test/unit/commands/wait.test.ts +++ b/test/unit/commands/wait.test.ts @@ -52,6 +52,7 @@ const TEST_CONTEXT = { logger: createLogger('info', () => undefined), profileDefault: undefined, rendererDefault: 'ghostty-web', + rendererVisualDefault: 'ghostty-web', explicitHome: false, configFile: null, } as const; diff --git a/test/unit/renderer/capabilities.test.ts b/test/unit/renderer/capabilities.test.ts index 6a2c5d83..e7026ea5 100644 --- a/test/unit/renderer/capabilities.test.ts +++ b/test/unit/renderer/capabilities.test.ts @@ -19,7 +19,7 @@ describe('discoverCapabilities', () => { probeLibghosttyVt, }); - expect(probePlaywright).toHaveBeenCalledTimes(2); + expect(probePlaywright).toHaveBeenCalledTimes(1); expect(probeLibghosttyVt).toHaveBeenCalledTimes(1); expect(capabilities).toHaveLength(6); expect(capabilities.map((capability) => capability.name)).toEqual([ @@ -100,12 +100,70 @@ describe('discoverCapabilities', () => { }); }); + it('uses ghostty-web as the quick semantic fallback when libghostty-vt is missing', async () => { + const capabilities = await discoverCapabilities('quick', { + probeLibghosttyVt: () => + Promise.resolve({ + available: false, + reason: 'libghostty-vt not installed', + detail: 'missing optional native package', + }), + probePlaywright: () => Promise.resolve({ available: true }), + }); + + expect(getCapability(capabilities, 'snapshot')).toEqual({ + name: 'snapshot', + status: 'available', + }); + expect(getCapability(capabilities, 'wait')).toEqual({ + name: 'wait', + status: 'available', + }); + }); + + it('marks quick semantic capabilities unavailable when no renderer can serve them', async () => { + const capabilities = await discoverCapabilities('quick', { + probeLibghosttyVt: () => + Promise.resolve({ + available: false, + reason: 'libghostty-vt not installed', + detail: 'missing optional native package', + }), + probePlaywright: () => + Promise.resolve({ + available: false, + reason: 'playwright not installed', + detail: 'missing browser renderer package', + }), + }); + + expect(getCapability(capabilities, 'snapshot')).toEqual({ + name: 'snapshot', + status: 'unavailable', + reason: 'semantic renderer unavailable', + detail: + 'missing optional native package; missing browser renderer package', + }); + expect(getCapability(capabilities, 'wait')).toEqual({ + name: 'wait', + status: 'unavailable', + reason: 'semantic renderer unavailable', + detail: + 'missing optional native package; missing browser renderer package', + }); + }); + it('uses full doctor renderer checks without re-probing playwright', async () => { const probePlaywright = vi.fn(() => Promise.resolve({ available: true })); const capabilities = await discoverCapabilities('full', { probePlaywright, rendererChecks: [ + { + name: 'libghostty_vt_available', + status: 'pass', + message: 'native available', + }, { name: 'playwright_available', status: 'pass', @@ -133,8 +191,8 @@ describe('discoverCapabilities', () => { expect(getCapability(capabilities, 'snapshot')).toEqual({ name: 'snapshot', status: 'available', - reason: 'built-in capability', - detail: 'available without external renderer dependencies', + reason: 'libghostty-vt semantic renderer available', + detail: 'native available', }); expect(getCapability(capabilities, 'screenshot')).toMatchObject({ name: 'screenshot', diff --git a/test/unit/renderer/libghosttyVtBackend.test.ts b/test/unit/renderer/libghosttyVtBackend.test.ts index bf01833f..a63ec105 100644 --- a/test/unit/renderer/libghosttyVtBackend.test.ts +++ b/test/unit/renderer/libghosttyVtBackend.test.ts @@ -170,6 +170,7 @@ describe('LibghosttyVtBackend', () => { expect(fixture.createTerminal).toHaveBeenCalledWith({ cols: 100, rows: 30, + scrollbackLimit: 10_000, }); expect(backend.isBooted).toBe(true); }); diff --git a/test/unit/renderer/registry.test.ts b/test/unit/renderer/registry.test.ts index ab06cfe9..825ceb62 100644 --- a/test/unit/renderer/registry.test.ts +++ b/test/unit/renderer/registry.test.ts @@ -2,6 +2,8 @@ import { describe, expect, it, vi } from 'vitest'; import { DEFAULT_RENDERER_NAME, + DEFAULT_SEMANTIC_RENDERER_NAME, + DEFAULT_VISUAL_RENDERER_NAME, createRendererBackend, resolveRendererName, } from '../../../src/renderer/index.js'; @@ -22,8 +24,10 @@ function createProfile(): RenderProfileConfig { } describe('renderer registry', () => { - it('resolves the default renderer name', () => { + it('resolves renderer default constants', () => { expect(DEFAULT_RENDERER_NAME).toBe('ghostty-web'); + expect(DEFAULT_SEMANTIC_RENDERER_NAME).toBe('libghostty-vt'); + expect(DEFAULT_VISUAL_RENDERER_NAME).toBe('ghostty-web'); expect(resolveRendererName(undefined)).toBe('ghostty-web'); }); From 5b13076afa422ec9b6a0c66c20b21fd4d24d181e Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Tue, 16 Jun 2026 13:29:23 +0000 Subject: [PATCH 2/3] fix: address renderer default review findings --- design/ARCHITECTURE.md | 2 +- docs/TROUBLESHOOTING.md | 4 +- .../README.md | 8 ++- .../commands.sh | 56 +++++++++------ src/cli/main.ts | 19 ++++-- src/config/defaults.ts | 1 + src/host/hostMain.ts | 68 +++++++++++-------- src/host/renderer.ts | 20 +++++- src/pty/createPty.ts | 9 ++- src/renderer/capabilities.ts | 56 +++++++++------ src/renderer/libghosttyVt/backend.ts | 51 +++++++------- test/integration/backend-selection.test.ts | 36 ++++++++++ test/integration/host-renderer-rpc.test.ts | 12 +++- test/unit/commands/wait.test.ts | 24 +++++++ test/unit/host/renderer.test.ts | 40 +++++++++++ test/unit/pty/createPty.test.ts | 10 +++ test/unit/renderer/capabilities.test.ts | 44 +++++++++++- .../unit/renderer/libghosttyVtBackend.test.ts | 3 +- 18 files changed, 349 insertions(+), 114 deletions(-) diff --git a/design/ARCHITECTURE.md b/design/ARCHITECTURE.md index cb988c5b..8895320b 100644 --- a/design/ARCHITECTURE.md +++ b/design/ARCHITECTURE.md @@ -21,7 +21,7 @@ This design intentionally describes a **general product**, not a Mux-specific im ## Current shipped status -The current `0.3.x` line is centered on reliable, isolated, reviewable terminal and TUI automation. The shipped surface includes `run` for robust in-session command execution, split semantic/visual renderer defaults, renderer/browser-path handling that respects isolated-home workflows, and isolation-aware `doctor --json` diagnostics on top of lifecycle, snapshot, screenshot, and export work. Larger asks such as additional native renderers, mouse input, remote/network sessions, MCP wrapping, and broader semantic TUI automation remain intentionally deferred. +The current shipped line is centered on reliable, isolated, reviewable terminal and TUI automation. The shipped surface includes `run` for robust in-session command execution, split semantic/visual renderer defaults, renderer/browser-path handling that respects isolated-home workflows, and isolation-aware `doctor --json` diagnostics on top of lifecycle, snapshot, screenshot, and export work. Larger asks such as additional native renderers, mouse input, remote/network sessions, MCP wrapping, and broader semantic TUI automation remain intentionally deferred. The repository now ships the first three milestones of this design plus Weeks 4–7 of CLI/artifact/lifecycle hardening, config/rendering/platform closeout, contract/introspection reconciliation, and Week 7 contract/doc ratification: diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index 01d64da1..c81b92c1 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -42,7 +42,7 @@ Check `doctor --json` for: - `ghostty_web_available` (visual renderer and semantic fallback) - `screenshot_viable` -If the browser-backed checks fail in CI or a container, install Chromium during setup and make sure the cache is readable by the process running `agent-tty`. If `libghostty_vt_available` is skipped or unavailable, semantic commands should fall back to `ghostty-web`; use `--renderer ghostty-web` to make that choice explicit. +If the browser-backed checks fail in CI or a container, install Chromium during setup and make sure the cache is readable by the process running `agent-tty`. If `libghostty_vt_available` is skipped or unavailable and no renderer is explicitly configured, semantic commands should fall back to `ghostty-web`; use `--renderer ghostty-web` to make that choice explicit. If you have `AGENT_TTY_RENDERER=libghostty-vt` or Home `config.json` sets `defaultRenderer` to `libghostty-vt`, clear that explicit configuration or override it with `ghostty-web` on machines without the optional native package. ## Isolated Homes @@ -78,7 +78,7 @@ or install a GitHub Release tarball as described in [`INSTALL.md`](./INSTALL.md) ## Reference Rendering Caveat -`libghostty-vt` is the preferred default for semantic snapshots and render-backed waits when the optional native package is available. `ghostty-web` remains the reference visual renderer for screenshots and replay video, and it is the semantic fallback when native rendering is unavailable. +`libghostty-vt` is the preferred default for semantic snapshots and render-backed waits when the optional native package is available. `ghostty-web` remains the reference visual renderer for screenshots and replay video, and it is the automatic semantic fallback when native rendering is unavailable and no renderer override is set. These renderers give repeatable artifacts for review and automation, but they do not guarantee exact native-terminal pixel parity. If a bug depends on a specific native terminal emulator, keep the `agent-tty` artifact as reference evidence and capture native-terminal evidence separately when needed. diff --git a/dogfood/20260616-default-semantic-renderer/README.md b/dogfood/20260616-default-semantic-renderer/README.md index d418adc2..a412783d 100644 --- a/dogfood/20260616-default-semantic-renderer/README.md +++ b/dogfood/20260616-default-semantic-renderer/README.md @@ -5,12 +5,12 @@ This bundle proves the split renderer default: semantic actions (`wait`, `snapsh ## What this proves - `expected-renderer.json` records the automatic semantic renderer detected in this workspace. In this capture it is `libghostty-vt`. -- `default-wait.json` was captured without `--renderer`, exercising the automatic semantic render-wait path. +- `default-wait.json` was captured without `--renderer`, exercising the automatic semantic render-wait path; `default-wait-inspect.json` records the live renderer runtime immediately after that wait. - `default-snapshot.json` was captured without `--renderer`; `default-snapshot-artifact.json` records the snapshot artifact metadata with `rendererBackend: "libghostty-vt"`. - `default-screenshot.json` was captured without `--renderer` and reports `result.rendererBackend: "ghostty-web"`; the PNG is copied to `screenshots/default-screenshot.png`. - `default-webm.json` was captured without `--renderer` and reports `result.metadata.rendererBackend: "ghostty-web"`; the WebM is copied to `videos/default-webm.webm`. - `explicit-ghostty-web-snapshot.json` proves the legacy override path; `explicit-ghostty-web-snapshot-artifact.json` records `rendererBackend: "ghostty-web"`. -- `explicit-libghostty-vt-screenshot.json` proves explicit native screenshot requests still produce honest `ghostty-web` PNG metadata via fallback. +- `explicit-libghostty-vt-screenshot.json` proves explicit native screenshot requests still produce honest `ghostty-web` PNG metadata via fallback when native support is available; in fallback-only environments the replay script writes an explicit skipped-evidence JSON instead. - `explicit-libghostty-vt-webm.json` proves explicit native WebM requests are accepted while the actual video producer remains `ghostty-web`. - `default-cast.json` and `recordings/default.cast` keep a terminal recording of the session. @@ -34,12 +34,14 @@ From the repository root: bash dogfood/20260616-default-semantic-renderer/commands.sh ``` -The script requires `git`, `jq`, `node`, `npm`, `npx`, and the installed project dependencies. It never writes to `~/.agent-tty`; every CLI command uses the temporary `AGENT_TTY_HOME` created at startup. +The script requires `git`, `jq`, `node`, `npm`, `npx`, and the installed project dependencies. Native `@coder/libghostty-vt-node` support is required only for the explicit native screenshot proof; when it is unavailable, the script still exercises automatic semantic fallback and writes skipped evidence for that native-only screenshot step. It never writes to `~/.agent-tty`; every CLI command uses the temporary `AGENT_TTY_HOME` created at startup. ## Reviewer checks ```bash jq -r '.expectedSemanticRenderer' dogfood/20260616-default-semantic-renderer/expected-renderer.json +jq -r '.result.rendererRuntime.backend' \ + dogfood/20260616-default-semantic-renderer/default-wait-inspect.json jq -r '.metadata.rendererBackend' \ dogfood/20260616-default-semantic-renderer/default-snapshot-artifact.json \ dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot-artifact.json diff --git a/dogfood/20260616-default-semantic-renderer/commands.sh b/dogfood/20260616-default-semantic-renderer/commands.sh index 8b27acae..d7b58495 100755 --- a/dogfood/20260616-default-semantic-renderer/commands.sh +++ b/dogfood/20260616-default-semantic-renderer/commands.sh @@ -13,6 +13,7 @@ PROMPT_TEXT='READY>' ECHO_TEXT='Default semantic renderer proof' AGENT_TTY_HOME="$(mktemp -d -t agent-tty-default-renderer.XXXXXX)" export AGENT_TTY_HOME +export NODE_OPTIONS="${NODE_OPTIONS:+$NODE_OPTIONS }--no-warnings" SESSION_ID='' require_command() { @@ -103,12 +104,14 @@ require_command npm require_command npx require_command uname +cd "$REPO_ROOT" +CAPTURE_GIT_STATUS="$(git status --short)" +CAPTURE_GIT_DIFF_SHA256="$(git diff --binary | sha256sum | awk '{print $1}')" + mkdir -p "$SCREENSHOTS_DIR" "$VIDEOS_DIR" "$RECORDINGS_DIR" rm -f "$BUNDLE_DIR"/*.json "$BUNDLE_DIR/environment.txt" rm -f "$SCREENSHOTS_DIR"/*.png "$VIDEOS_DIR"/*.webm "$RECORDINGS_DIR"/*.cast -cd "$REPO_ROOT" - EXPECTED_SEMANTIC_RENDERER="$({ node --input-type=module <<'NODE' try { @@ -130,6 +133,8 @@ printf '{"expectedSemanticRenderer":"%s"}\n' "$EXPECTED_SEMANTIC_RENDERER" | jq printf '$ npm --version\n%s\n\n' "$(npm --version)" printf '$ git rev-parse HEAD\n%s\n\n' "$(git rev-parse HEAD)" printf '$ git log --oneline -n 1\n%s\n\n' "$(git log --oneline -n 1)" + printf '$ git status --short (before capture)\n%s\n\n' "${CAPTURE_GIT_STATUS:-clean}" + printf '$ git diff --binary | sha256sum (before capture)\n%s\n\n' "$CAPTURE_GIT_DIFF_SHA256" printf '$ uname -a\n%s\n\n' "$(uname -a)" printf '$ expected semantic renderer\n%s\n\n' "$EXPECTED_SEMANTIC_RENDERER" printf '$ npx tsx src/cli/main.ts version --json\n' @@ -154,6 +159,8 @@ run_json_check_only "${CLI[@]}" --home "$AGENT_TTY_HOME" type "$SESSION_ID" --js run_json_check_only "${CLI[@]}" --home "$AGENT_TTY_HOME" send-keys "$SESSION_ID" --json Enter run_json_file "$BUNDLE_DIR/default-wait.json" \ "${CLI[@]}" --home "$AGENT_TTY_HOME" wait "$SESSION_ID" --json --text "ECHO: $ECHO_TEXT" --timeout 10000 +run_json_file "$BUNDLE_DIR/default-wait-inspect.json" "${CLI[@]}" --home "$AGENT_TTY_HOME" inspect "$SESSION_ID" --json +jq -e --arg backend "$EXPECTED_SEMANTIC_RENDERER" '.result.rendererRuntime.backend == $backend' "$BUNDLE_DIR/default-wait-inspect.json" >/dev/null run_json_check_only "${CLI[@]}" --home "$AGENT_TTY_HOME" wait "$SESSION_ID" --json --screen-stable-ms 250 --timeout 10000 run_json_file "$BUNDLE_DIR/default-snapshot.json" \ @@ -174,48 +181,53 @@ run_json_file "$BUNDLE_DIR/explicit-ghostty-web-snapshot.json" \ assert_latest_artifact_backend snapshot ghostty-web write_latest_artifact snapshot "$BUNDLE_DIR/explicit-ghostty-web-snapshot-artifact.json" -run_json_file "$BUNDLE_DIR/explicit-libghostty-vt-screenshot.json" \ - "${CLI[@]}" --home "$AGENT_TTY_HOME" --renderer libghostty-vt screenshot "$SESSION_ID" --hide-cursor --json -jq -e '.result.rendererBackend == "ghostty-web"' "$BUNDLE_DIR/explicit-libghostty-vt-screenshot.json" >/dev/null -EXPLICIT_SCREENSHOT_SOURCE="$(jq -er '.result.artifactPath' "$BUNDLE_DIR/explicit-libghostty-vt-screenshot.json")" -assert_file_nonempty "$EXPLICIT_SCREENSHOT_SOURCE" -cp "$EXPLICIT_SCREENSHOT_SOURCE" "$SCREENSHOTS_DIR/explicit-libghostty-vt-screenshot.png" -assert_file_nonempty "$SCREENSHOTS_DIR/explicit-libghostty-vt-screenshot.png" +if [[ "$EXPECTED_SEMANTIC_RENDERER" == 'libghostty-vt' ]]; then + run_json_file "$BUNDLE_DIR/explicit-libghostty-vt-screenshot.json" \ + "${CLI[@]}" --home "$AGENT_TTY_HOME" --renderer libghostty-vt screenshot "$SESSION_ID" --hide-cursor --json + jq -e '.result.rendererBackend == "ghostty-web"' "$BUNDLE_DIR/explicit-libghostty-vt-screenshot.json" >/dev/null + EXPLICIT_SCREENSHOT_SOURCE="$(jq -er '.result.artifactPath' "$BUNDLE_DIR/explicit-libghostty-vt-screenshot.json")" + assert_file_nonempty "$EXPLICIT_SCREENSHOT_SOURCE" + cp "$EXPLICIT_SCREENSHOT_SOURCE" "$SCREENSHOTS_DIR/explicit-libghostty-vt-screenshot.png" + assert_file_nonempty "$SCREENSHOTS_DIR/explicit-libghostty-vt-screenshot.png" +else + jq -n '{ok: true, command: "explicit-libghostty-vt-screenshot", result: {skipped: true, reason: "libghostty-vt optional renderer unavailable"}}' \ + > "$BUNDLE_DIR/explicit-libghostty-vt-screenshot.json" +fi run_json_check_only "${CLI[@]}" --home "$AGENT_TTY_HOME" type "$SESSION_ID" --json 'exit' run_json_check_only "${CLI[@]}" --home "$AGENT_TTY_HOME" send-keys "$SESSION_ID" --json Enter run_json_check_only "${CLI[@]}" --home "$AGENT_TTY_HOME" wait "$SESSION_ID" --json --exit --timeout 10000 run_json_file "$BUNDLE_DIR/default-webm.json" \ - "${CLI[@]}" --home "$AGENT_TTY_HOME" record export "$SESSION_ID" --format webm --timing accelerated --json + "${CLI[@]}" --home "$AGENT_TTY_HOME" record export "$SESSION_ID" --format webm --timing accelerated --out "$VIDEOS_DIR/default-webm.webm" --json jq -e '.result.metadata.rendererBackend == "ghostty-web"' "$BUNDLE_DIR/default-webm.json" >/dev/null DEFAULT_WEBM_SOURCE="$(jq -er '.result.artifactPath' "$BUNDLE_DIR/default-webm.json")" -assert_file_nonempty "$DEFAULT_WEBM_SOURCE" -cp "$DEFAULT_WEBM_SOURCE" "$VIDEOS_DIR/default-webm.webm" +[[ "$DEFAULT_WEBM_SOURCE" == "$VIDEOS_DIR/default-webm.webm" ]] assert_file_nonempty "$VIDEOS_DIR/default-webm.webm" run_json_file "$BUNDLE_DIR/explicit-libghostty-vt-webm.json" \ - "${CLI[@]}" --home "$AGENT_TTY_HOME" --renderer libghostty-vt record export "$SESSION_ID" --format webm --timing accelerated --json + "${CLI[@]}" --home "$AGENT_TTY_HOME" --renderer libghostty-vt record export "$SESSION_ID" --format webm --timing accelerated --out "$VIDEOS_DIR/explicit-libghostty-vt-webm.webm" --json jq -e '.result.metadata.rendererBackend == "ghostty-web"' "$BUNDLE_DIR/explicit-libghostty-vt-webm.json" >/dev/null EXPLICIT_WEBM_SOURCE="$(jq -er '.result.artifactPath' "$BUNDLE_DIR/explicit-libghostty-vt-webm.json")" -assert_file_nonempty "$EXPLICIT_WEBM_SOURCE" -cp "$EXPLICIT_WEBM_SOURCE" "$VIDEOS_DIR/explicit-libghostty-vt-webm.webm" +[[ "$EXPLICIT_WEBM_SOURCE" == "$VIDEOS_DIR/explicit-libghostty-vt-webm.webm" ]] assert_file_nonempty "$VIDEOS_DIR/explicit-libghostty-vt-webm.webm" run_json_file "$BUNDLE_DIR/default-cast.json" \ - "${CLI[@]}" --home "$AGENT_TTY_HOME" record export "$SESSION_ID" --format asciicast --json + "${CLI[@]}" --home "$AGENT_TTY_HOME" record export "$SESSION_ID" --format asciicast --out "$RECORDINGS_DIR/default.cast" --json CAST_SOURCE="$(jq -er '.result.artifactPath' "$BUNDLE_DIR/default-cast.json")" -assert_file_nonempty "$CAST_SOURCE" -cp "$CAST_SOURCE" "$RECORDINGS_DIR/default.cast" +[[ "$CAST_SOURCE" == "$RECORDINGS_DIR/default.cast" ]] assert_file_nonempty "$RECORDINGS_DIR/default.cast" run_json_file "$BUNDLE_DIR/inspect.json" "${CLI[@]}" --home "$AGENT_TTY_HOME" inspect "$SESSION_ID" --json copy_artifact_manifest -file "$SCREENSHOTS_DIR"/*.png > "$BUNDLE_DIR/artifact-file-info.txt" -file "$VIDEOS_DIR"/*.webm >> "$BUNDLE_DIR/artifact-file-info.txt" -file "$RECORDINGS_DIR"/*.cast >> "$BUNDLE_DIR/artifact-file-info.txt" -sha256sum "$SCREENSHOTS_DIR"/*.png "$VIDEOS_DIR"/*.webm "$RECORDINGS_DIR"/*.cast > "$BUNDLE_DIR/artifact-sha256.txt" +( + cd "$BUNDLE_DIR" + file screenshots/*.png > artifact-file-info.txt + file videos/*.webm >> artifact-file-info.txt + file recordings/*.cast >> artifact-file-info.txt + sha256sum screenshots/*.png videos/*.webm recordings/*.cast > artifact-sha256.txt +) run_json_check_only "${CLI[@]}" --home "$AGENT_TTY_HOME" destroy "$SESSION_ID" --json SESSION_ID='' diff --git a/src/cli/main.ts b/src/cli/main.ts index 1c299b45..665dc828 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -46,6 +46,7 @@ import { DEFAULT_ROWS, DEFAULT_SHELL, DEFAULT_TERM, + HOST_RENDERER_ENV_KEY, } from '../config/defaults.js'; import { ERROR_CODES, makeCliError } from '../protocol/errors.js'; import { invariant } from '../util/assert.js'; @@ -143,9 +144,10 @@ async function main(): Promise { ); program.hook('preAction', async (_thisCommand, actionCommand) => { - const context = await resolveCommandContext( - actionCommand.optsWithGlobals(), - ); + const globalOptions = actionCommand.optsWithGlobals(); + const rendererConfiguredByEnv = + process.env.AGENT_TTY_RENDERER !== undefined; + const context = await resolveCommandContext(globalOptions); process.env.AGENT_TTY_HOME = context.home; // Propagate the resolved log level to the process environment so that // subsystems instantiated outside the CLI context (e.g., renderer backends, @@ -154,7 +156,16 @@ async function main(): Promise { // and constructor is a larger refactor with no user-visible benefit, since // the env var is set before any command handler runs. process.env.AGENT_TTY_LOG_LEVEL = context.logLevel; - process.env.AGENT_TTY_RENDERER = context.rendererDefault; + process.env[HOST_RENDERER_ENV_KEY] = context.rendererDefault; + const rendererConfiguredExplicitly = + globalOptions.renderer !== undefined || + rendererConfiguredByEnv || + context.configFile?.defaultRenderer !== undefined; + if (rendererConfiguredExplicitly) { + process.env.AGENT_TTY_RENDERER = context.rendererDefault; + } else { + delete process.env.AGENT_TTY_RENDERER; + } setColorEnabled(context.colorEnabled); setCommandContext(actionCommand, context); context.logger.debug('resolved command context', { diff --git a/src/config/defaults.ts b/src/config/defaults.ts index 907a9c1e..ffcb5b14 100644 --- a/src/config/defaults.ts +++ b/src/config/defaults.ts @@ -6,6 +6,7 @@ export const DEFAULT_TERM = 'xterm-256color'; export const DEFAULT_SHELL = process.env.SHELL ?? '/bin/sh'; export { DEFAULT_LOG_LEVEL } from '../util/logger.js'; export const DEFAULT_IDLE_TIMEOUT_MS = 0 as const; +export const HOST_RENDERER_ENV_KEY = 'AGENT_TTY_HOST_RENDERER' as const; export const SOCKET_FILENAME = 'host.sock'; export const MANIFEST_FILENAME = 'session.json'; diff --git a/src/host/hostMain.ts b/src/host/hostMain.ts index dba6d08d..bdd65592 100644 --- a/src/host/hostMain.ts +++ b/src/host/hostMain.ts @@ -37,6 +37,7 @@ import { canonicalVisibleText, computeScreenHash, } from '../renderer/canonicalScreen.js'; +import { HOST_RENDERER_ENV_KEY } from '../config/defaults.js'; import { DEFAULT_RENDERER_NAME, resolveRendererName, @@ -113,14 +114,17 @@ function rethrowAsync(error: unknown): void { } function resolveHostRendererName(input: string | undefined): RendererName { + const rawRenderer = + input ?? + process.env[HOST_RENDERER_ENV_KEY] ?? + process.env.AGENT_TTY_RENDERER ?? + DEFAULT_RENDERER_NAME; try { - return resolveRendererName( - input ?? process.env.AGENT_TTY_RENDERER ?? DEFAULT_RENDERER_NAME, - ); + return resolveRendererName(rawRenderer); } catch (error) { throw makeCliError(ERROR_CODES.INVALID_INPUT, { message: 'Renderer must be one of: ghostty-web, libghostty-vt.', - details: { renderer: input ?? process.env.AGENT_TTY_RENDERER }, + details: { renderer: rawRenderer }, cause: error, }); } @@ -257,15 +261,17 @@ export async function runHost(sessionId: string): Promise { const rendererName = resolveHostRendererName(undefined); const profile = resolveProfile(DEFAULT_RENDER_PROFILE_NAME); - const backend = await rendererManager.getBackend( + await rendererManager.withBackend( rendererName, profile, replayInput, - ); - const snapshot = await backend.snapshot(); - invariant( - snapshot.capturedAtSeq >= targetSeq, - 'renderer snapshot must include the run-complete event sequence', + async (backend) => { + const snapshot = await backend.snapshot(); + invariant( + snapshot.capturedAtSeq >= targetSeq, + 'renderer snapshot must include the run-complete event sequence', + ); + }, ); }; @@ -479,20 +485,23 @@ export async function runHost(sessionId: string): Promise { const rendererName = resolveHostRendererName(requestedRendererName); const profile = resolveProfile(DEFAULT_RENDER_PROFILE_NAME); const replayInput = loadReplayInput(); - const backend = await rendererManager.getBackend( + const { snapshot, rendererBackend } = await rendererManager.withBackend( rendererName, profile, replayInput, + async (backend) => ({ + snapshot: await backend.snapshot({ + includeScrollback, + includeCells, + }), + rendererBackend: backend.rendererBackend, + }), ); - const snapshot = await backend.snapshot({ - includeScrollback, - includeCells, - }); return await captureSnapshotResult({ sessionDir: sessDir, format, snapshot, - rendererBackend: backend.rendererBackend, + rendererBackend, expectedSessionId: sessionId, }); }, @@ -524,19 +533,20 @@ export async function runHost(sessionId: string): Promise { const rendererName = resolveHostRendererName(requestedRendererName); const replayInput = loadReplayInput(); - const backend = await rendererManager.getBackend( + + return await rendererManager.withBackend( rendererName, profile, replayInput, + async (backend) => + await captureScreenshotResult({ + backend, + sessionDir: sessDir, + profileName: profile.name, + expectedSessionId: sessionId, + ...(showCursor === undefined ? {} : { showCursor }), + }), ); - - return await captureScreenshotResult({ - backend, - sessionDir: sessDir, - profileName: profile.name, - expectedSessionId: sessionId, - ...(showCursor === undefined ? {} : { showCursor }), - }); }, type: async (params: unknown) => { const { text } = params as TypeParams; @@ -901,14 +911,16 @@ export async function runHost(sessionId: string): Promise { try { throwIfAborted(signal); const replayInput = loadReplayInput(); - const backend = await rendererManager.getBackend( + const snapshot = await rendererManager.withBackend( rendererName, profile, replayInput, + async (backend) => { + throwIfAborted(signal); + return await backend.snapshot(); + }, ); throwIfAborted(signal); - const snapshot = await backend.snapshot(); - throwIfAborted(signal); const visibleText = canonicalVisibleText(snapshot); const capturedAtSeq = snapshot.capturedAtSeq; latestCapturedAtSeq = capturedAtSeq; diff --git a/src/host/renderer.ts b/src/host/renderer.ts index 8fe0dac3..d4ab27da 100644 --- a/src/host/renderer.ts +++ b/src/host/renderer.ts @@ -64,8 +64,26 @@ export class HostRendererManager { profile: RenderProfileConfig, replayInput: ReplayInput | null, ): Promise { + return await this.withBackend( + rendererName, + profile, + replayInput, + (backend) => backend, + ); + } + + async withBackend( + rendererName: RendererName, + profile: RenderProfileConfig, + replayInput: ReplayInput | null, + operation: (backend: RendererBackend) => T | Promise, + ): Promise { assertNonEmptyString(rendererName, 'rendererName'); assertNonEmptyString(profile.name, 'profile name'); + invariant( + typeof operation === 'function', + 'backend operation must be a function', + ); if (replayInput !== null) { invariant( @@ -94,7 +112,7 @@ export class HostRendererManager { this.cachedInitialRows = replayInput.initialRows; } - return backend; + return await operation(backend); }); } diff --git a/src/pty/createPty.ts b/src/pty/createPty.ts index 2e3f0b11..fbf50057 100644 --- a/src/pty/createPty.ts +++ b/src/pty/createPty.ts @@ -5,6 +5,8 @@ import process from 'node:process'; import type { IPty } from 'node-pty'; import { spawn } from 'node-pty'; + +import { HOST_RENDERER_ENV_KEY } from '../config/defaults.js'; import { invariant } from '../util/assert.js'; export interface PtyOptions { @@ -82,8 +84,8 @@ const PROMPT_EOL_MARK_ENV_KEY = 'PROMPT_EOL_MARK'; /** * Resolves the environment handed to the spawned PTY shell. * - * Precedence, lowest to highest: the inherited process environment, then the - * `PROMPT_EOL_MARK=''` default, then the caller-supplied `env` (so a `--env` + * Precedence, lowest to highest: the inherited process environment (minus + * host-only internals), then the `PROMPT_EOL_MARK=''` default, then the caller-supplied `env` (so a `--env` * value always wins — even an explicit empty one), then `TERM`. The default sits * after the inherited environment so it also overrides any inherited * `PROMPT_EOL_MARK`, keeping captures deterministic regardless of the launching @@ -97,6 +99,9 @@ export function resolvePtyEnv( ): Record { const resolved: Record = {}; for (const [key, value] of Object.entries(baseEnv)) { + if (key === HOST_RENDERER_ENV_KEY) { + continue; + } if (value !== undefined) { resolved[key] = value; } diff --git a/src/renderer/capabilities.ts b/src/renderer/capabilities.ts index a9bcf3fe..f5eebde3 100644 --- a/src/renderer/capabilities.ts +++ b/src/renderer/capabilities.ts @@ -226,6 +226,30 @@ function buildSemanticUnavailableDetail( return details.length === 0 ? undefined : details.join('; '); } +function buildUnavailableSemanticRenderCapability( + name: 'snapshot' | 'wait', + detail: string | undefined, +): CapabilityEntry { + if (name === 'wait') { + return { + name, + status: 'degraded', + reason: 'render waits unavailable', + detail: + detail === undefined + ? 'legacy --exit and --idle-ms wait modes remain available' + : `legacy --exit and --idle-ms wait modes remain available; ${detail}`, + }; + } + + return { + name, + status: 'unavailable', + reason: 'semantic renderer unavailable', + detail, + }; +} + async function buildSemanticRenderCapability( name: 'snapshot' | 'wait', mode: DiscoveryMode, @@ -267,12 +291,10 @@ async function buildSemanticRenderCapability( : { name, status: 'available' }; } - return { + return buildUnavailableSemanticRenderCapability( name, - status: 'unavailable', - reason: 'semantic renderer unavailable', - detail: buildSemanticUnavailableDetail(libghosttyVtProbe, playwrightProbe), - }; + buildSemanticUnavailableDetail(libghosttyVtProbe, playwrightProbe), + ); } function buildFullSemanticRenderCapabilityFromChecks( @@ -309,30 +331,24 @@ function buildFullSemanticRenderCapabilityFromChecks( } if (playwrightCheck.status === 'fail') { - return { + return buildUnavailableSemanticRenderCapability( name, - status: 'unavailable', - reason: 'semantic renderer unavailable', - detail: `libghostty-vt: ${libghosttyVtCheck.message}; playwright: ${playwrightCheck.message}`, - }; + `libghostty-vt: ${libghosttyVtCheck.message}; playwright: ${playwrightCheck.message}`, + ); } if (ghosttyWebCheck.status === 'fail') { - return { + return buildUnavailableSemanticRenderCapability( name, - status: 'unavailable', - reason: 'semantic renderer unavailable', - detail: `libghostty-vt: ${libghosttyVtCheck.message}; ghostty-web: ${ghosttyWebCheck.message}`, - }; + `libghostty-vt: ${libghosttyVtCheck.message}; ghostty-web: ${ghosttyWebCheck.message}`, + ); } if (browserLaunchCheck.status === 'fail') { - return { + return buildUnavailableSemanticRenderCapability( name, - status: 'unavailable', - reason: 'semantic renderer unavailable', - detail: `libghostty-vt: ${libghosttyVtCheck.message}; browser: ${browserLaunchCheck.message}`, - }; + `libghostty-vt: ${libghosttyVtCheck.message}; browser: ${browserLaunchCheck.message}`, + ); } return { diff --git a/src/renderer/libghosttyVt/backend.ts b/src/renderer/libghosttyVt/backend.ts index d8c7255f..fc17adb1 100644 --- a/src/renderer/libghosttyVt/backend.ts +++ b/src/renderer/libghosttyVt/backend.ts @@ -378,7 +378,6 @@ export class LibghosttyVtBackend implements RendererBackend { private currentCols: number; private currentRows: number; private disposed = false; - private fallbackBackend: RendererBackend | null = null; private initialReplayCols: number | null = null; private initialReplayRows: number | null = null; private lastAppliedSeq = -1; @@ -605,9 +604,13 @@ export class LibghosttyVtBackend implements RendererBackend { ); invariant(isAbsolute(outputPath), 'screenshot outputPath must be absolute'); - const fallback = await this.ensureFallbackBackend(); - await fallback.replayTo(this.latestReplayInput); - return await fallback.screenshot(outputPath, options); + const fallback = await this.createFallbackBackend(); + try { + await fallback.replayTo(this.latestReplayInput); + return await fallback.screenshot(outputPath, options); + } finally { + await fallback.dispose(); + } } public async getVisibleText(): Promise { @@ -618,9 +621,9 @@ export class LibghosttyVtBackend implements RendererBackend { return visibleText; } - public async dispose(): Promise { + public dispose(): Promise { if (this.disposed) { - return; + return Promise.resolve(); } this.disposed = true; @@ -630,12 +633,7 @@ export class LibghosttyVtBackend implements RendererBackend { if (terminal !== null) { terminal.dispose(); } - - const fallback = this.fallbackBackend; - this.fallbackBackend = null; - if (fallback !== null) { - await fallback.dispose(); - } + return Promise.resolve(); } private async bootInternal(): Promise { @@ -677,20 +675,23 @@ export class LibghosttyVtBackend implements RendererBackend { } } - private async ensureFallbackBackend(): Promise { - if (this.fallbackBackend === null) { - const fallback = this.fallbackFactory(this.sessionId, this.profile); - invariant( - fallback.rendererBackend !== this.rendererBackend, - 'libghostty-vt screenshot fallback must use a different renderer backend', - ); - this.fallbackBackend = fallback; - } - - if (!this.fallbackBackend.isBooted) { - await this.fallbackBackend.boot(); + private async createFallbackBackend(): Promise { + const fallback = this.fallbackFactory(this.sessionId, this.profile); + invariant( + fallback.rendererBackend !== this.rendererBackend, + 'libghostty-vt screenshot fallback must use a different renderer backend', + ); + try { + await fallback.boot(); + return fallback; + } catch (error) { + try { + await fallback.dispose(); + } catch { + // Preserve the original fallback boot error; dispose is best effort. + } + throw error; } - return this.fallbackBackend; } private assertNotDisposed(methodName: string): void { diff --git a/test/integration/backend-selection.test.ts b/test/integration/backend-selection.test.ts index 5619ff6a..6cbacf40 100644 --- a/test/integration/backend-selection.test.ts +++ b/test/integration/backend-selection.test.ts @@ -8,6 +8,7 @@ import { cleanupHome, createSession, destroySession, + readEvents, runCli, } from '../helpers.js'; import { probeLibghosttyVt } from '../../src/renderer/readiness.js'; @@ -124,6 +125,41 @@ describe('backend selection integration', () => { }); }); + it('does not leak automatic renderer defaults into PTY environments', async () => { + sessionId = createSession(testHome, [ + '/bin/sh', + '-c', + 'printf "public=%s private=%s\\n" "${AGENT_TTY_RENDERER-unset}" "${AGENT_TTY_HOST_RENDERER-unset}"; exec cat', + ]); + + const result = runCli( + [ + 'wait', + sessionId, + '--text', + 'public=unset private=unset', + '--timeout', + '10000', + '--json', + ], + { AGENT_TTY_HOME: testHome }, + 60_000, + ); + + expect(result.status).toBe(0); + expect(result.stderr).toBe(''); + expect(JSON.parse(result.stdout)).toMatchObject({ + ok: true, + result: { matched: true, timedOut: false }, + }); + + const output = (await readEvents(testHome, sessionId)) + .filter((event) => event.type === 'output') + .map((event) => event.payload.data) + .join(''); + expect(output).toContain('public=unset private=unset'); + }); + it('threads --renderer ghostty-web through live snapshot RPC paths', () => { sessionId = createSession(testHome, [ '/bin/sh', diff --git a/test/integration/host-renderer-rpc.test.ts b/test/integration/host-renderer-rpc.test.ts index 8e3cdd21..7ec4b147 100644 --- a/test/integration/host-renderer-rpc.test.ts +++ b/test/integration/host-renderer-rpc.test.ts @@ -373,10 +373,18 @@ describe( sendRpc( rpcSocketPath, 'snapshot', - { format: 'structured' }, + { + format: 'structured', + rendererName: expectedSemanticRenderer, + }, + SNAPSHOT_TIMEOUT_MS, + ), + sendRpc( + rpcSocketPath, + 'screenshot', + { rendererName: 'ghostty-web' }, SNAPSHOT_TIMEOUT_MS, ), - sendRpc(rpcSocketPath, 'screenshot', {}, SNAPSHOT_TIMEOUT_MS), ])) as [SnapshotResult, ScreenshotResult]; const screenshotStats = await stat(screenshot.artifactPath); const manifest = await readArtifactManifest(sessDir); diff --git a/test/unit/commands/wait.test.ts b/test/unit/commands/wait.test.ts index d3953a2c..fb63e0bd 100644 --- a/test/unit/commands/wait.test.ts +++ b/test/unit/commands/wait.test.ts @@ -415,6 +415,30 @@ describe('wait command', () => { ); }); + it('passes the semantic renderer default to render wait RPCs', async () => { + const result = { + matched: true, + timedOut: false, + matchedText: 'hello', + capturedAtSeq: 7, + }; + mocks.sendRpc.mockResolvedValue(result); + + await runWaitCommand( + createOptions({ + context: { ...TEST_CONTEXT, rendererDefault: 'libghostty-vt' }, + text: 'hello', + }), + ); + + expect(mocks.sendRpc).toHaveBeenCalledWith( + '/tmp/agent-tty/sessions/session-01/rpc.sock', + 'waitForRender', + expect.objectContaining({ rendererName: 'libghostty-vt' }), + 605_000, + ); + }); + it('routes --regex waits to the render wait RPC', async () => { const result = { matched: true, diff --git a/test/unit/host/renderer.test.ts b/test/unit/host/renderer.test.ts index a506015b..8b4cba3a 100644 --- a/test/unit/host/renderer.test.ts +++ b/test/unit/host/renderer.test.ts @@ -210,6 +210,46 @@ describe('HostRendererManager', () => { expect(getCreatedBackend(backends, 1)).toBe(secondBackend); }); + it('keeps a backend leased until the operation completes', async () => { + const manager = new HostRendererManager({ + sessionId: 'session-01', + sessionDir, + backendFactory, + }); + const operationStarted = createDeferred(); + const releaseOperation = createDeferred(); + + const firstOperation = manager.withBackend( + 'libghostty-vt', + createProfile('dark'), + null, + async () => { + operationStarted.resolve(undefined); + await releaseOperation.promise; + return 'leased'; + }, + ); + await operationStarted.promise; + + const replacementOperation = manager.withBackend( + 'ghostty-web', + createProfile('dark'), + null, + (backend) => backend.rendererBackend, + ); + await flushAsyncQueue(); + + expect(backendFactory).toHaveBeenCalledTimes(1); + expect(getCreatedBackend(backends, 0).disposeMock).not.toHaveBeenCalled(); + + releaseOperation.resolve(undefined); + + await expect(firstOperation).resolves.toBe('leased'); + await expect(replacementOperation).resolves.toBe('fake-renderer'); + expect(backendFactory).toHaveBeenCalledTimes(2); + expect(getCreatedBackend(backends, 0).disposeMock).toHaveBeenCalledTimes(1); + }); + it('skips replay when the replay target sequence is -1', async () => { const manager = new HostRendererManager({ sessionId: 'session-01', diff --git a/test/unit/pty/createPty.test.ts b/test/unit/pty/createPty.test.ts index 51c8b535..62c0e711 100644 --- a/test/unit/pty/createPty.test.ts +++ b/test/unit/pty/createPty.test.ts @@ -44,6 +44,16 @@ describe('resolvePtyEnv', () => { expect(resolved.TERM).toBe('vt100'); }); + it('strips host-only renderer defaults from inherited env', () => { + const resolved = resolvePtyEnv({}, 'xterm-256color', { + AGENT_TTY_HOST_RENDERER: 'libghostty-vt', + AGENT_TTY_RENDERER: 'ghostty-web', + }); + + expect(resolved.AGENT_TTY_HOST_RENDERER).toBeUndefined(); + expect(resolved.AGENT_TTY_RENDERER).toBe('ghostty-web'); + }); + it('passes through inherited and caller env entries and drops undefined values', () => { const resolved = resolvePtyEnv({ FOO: 'bar' }, 'xterm-256color', { BAZ: 'qux', diff --git a/test/unit/renderer/capabilities.test.ts b/test/unit/renderer/capabilities.test.ts index e7026ea5..c63e3516 100644 --- a/test/unit/renderer/capabilities.test.ts +++ b/test/unit/renderer/capabilities.test.ts @@ -146,10 +146,10 @@ describe('discoverCapabilities', () => { }); expect(getCapability(capabilities, 'wait')).toEqual({ name: 'wait', - status: 'unavailable', - reason: 'semantic renderer unavailable', + status: 'degraded', + reason: 'render waits unavailable', detail: - 'missing optional native package; missing browser renderer package', + 'legacy --exit and --idle-ms wait modes remain available; missing optional native package; missing browser renderer package', }); }); @@ -209,6 +209,44 @@ describe('discoverCapabilities', () => { }); }); + it('keeps full wait degraded when render dependencies are unavailable', async () => { + const capabilities = await discoverCapabilities('full', { + rendererChecks: [ + { + name: 'libghostty_vt_available', + status: 'skip', + message: 'native missing', + }, + { + name: 'playwright_available', + status: 'fail', + message: 'playwright missing', + }, + { + name: 'browser_launch', + status: 'skip', + message: 'not attempted', + }, + { + name: 'ghostty_web_available', + status: 'skip', + message: 'not attempted', + }, + ], + }); + + expect(getCapability(capabilities, 'snapshot')).toMatchObject({ + name: 'snapshot', + status: 'unavailable', + reason: 'semantic renderer unavailable', + }); + expect(getCapability(capabilities, 'wait')).toMatchObject({ + name: 'wait', + status: 'degraded', + reason: 'render waits unavailable', + }); + }); + it('reports degraded full browser-backed capabilities from failing renderer checks', async () => { const capabilities = await discoverCapabilities('full', { rendererChecks: [ diff --git a/test/unit/renderer/libghosttyVtBackend.test.ts b/test/unit/renderer/libghosttyVtBackend.test.ts index a63ec105..f9eeee76 100644 --- a/test/unit/renderer/libghosttyVtBackend.test.ts +++ b/test/unit/renderer/libghosttyVtBackend.test.ts @@ -481,10 +481,11 @@ describe('LibghosttyVtBackend', () => { showCursor: true, }, ); + expect(fallback.disposeMock).toHaveBeenCalledTimes(1); expect(result.rendererBackend).toBe('ghostty-web'); }); - it('disposes native and fallback resources idempotently', async () => { + it('disposes native resources idempotently after screenshot fallback cleanup', async () => { const fixture = createNativeFixture(); const fallback = createFakeBackend({ rendererBackend: 'ghostty-web', From dabb79c58dc17d69321345f4c5836b59e58c5116 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Tue, 16 Jun 2026 13:31:02 +0000 Subject: [PATCH 3/3] test: refresh renderer default dogfood proof --- .../artifact-file-info.txt | 10 +- .../artifact-manifest.json | 100 +++++++++--------- .../artifact-sha256.txt | 10 +- .../create.json | 6 +- .../default-cast.json | 16 +-- .../default-screenshot.json | 12 +-- .../default-snapshot-artifact.json | 12 +-- .../default-snapshot.json | 22 ++-- .../default-wait-inspect.json | 51 +++++++++ .../default-wait.json | 8 +- .../default-webm.json | 12 +-- .../doctor.json | 26 ++--- .../environment.txt | 40 ++++--- ...xplicit-ghostty-web-snapshot-artifact.json | 12 +-- .../explicit-ghostty-web-snapshot.json | 12 +-- .../explicit-libghostty-vt-screenshot.json | 12 +-- .../explicit-libghostty-vt-webm.json | 12 +-- .../inspect.json | 37 +++++-- .../recordings/default.cast | 20 ++-- .../screenshots/default-screenshot.png | Bin 13319 -> 6810 bytes .../explicit-libghostty-vt-screenshot.png | Bin 13319 -> 6810 bytes .../version.json | 2 +- .../videos/default-webm.webm | Bin 49225 -> 26115 bytes .../videos/explicit-libghostty-vt-webm.webm | Bin 49695 -> 27083 bytes 24 files changed, 253 insertions(+), 179 deletions(-) create mode 100644 dogfood/20260616-default-semantic-renderer/default-wait-inspect.json diff --git a/dogfood/20260616-default-semantic-renderer/artifact-file-info.txt b/dogfood/20260616-default-semantic-renderer/artifact-file-info.txt index 9fc754fd..a79084cd 100644 --- a/dogfood/20260616-default-semantic-renderer/artifact-file-info.txt +++ b/dogfood/20260616-default-semantic-renderer/artifact-file-info.txt @@ -1,5 +1,5 @@ -/home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/screenshots/default-screenshot.png: PNG image data, 640 x 384, 8-bit/color RGB, non-interlaced -/home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/screenshots/explicit-libghostty-vt-screenshot.png: PNG image data, 640 x 384, 8-bit/color RGB, non-interlaced -/home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/videos/default-webm.webm: WebM -/home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/videos/explicit-libghostty-vt-webm.webm: WebM -/home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/recordings/default.cast: JSON data +screenshots/default-screenshot.png: PNG image data, 640 x 384, 8-bit/color RGB, non-interlaced +screenshots/explicit-libghostty-vt-screenshot.png: PNG image data, 640 x 384, 8-bit/color RGB, non-interlaced +videos/default-webm.webm: WebM +videos/explicit-libghostty-vt-webm.webm: WebM +recordings/default.cast: JSON data diff --git a/dogfood/20260616-default-semantic-renderer/artifact-manifest.json b/dogfood/20260616-default-semantic-renderer/artifact-manifest.json index 5e892480..22be4c4a 100644 --- a/dogfood/20260616-default-semantic-renderer/artifact-manifest.json +++ b/dogfood/20260616-default-semantic-renderer/artifact-manifest.json @@ -1,36 +1,36 @@ { "version": 1, - "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", "artifacts": [ { - "id": "01KV851Q0MS2YQW4MTWXYNJBBD", + "id": "01KV89ZCN99JFHWA7DZ0FDQV3V", "kind": "snapshot", - "filename": "snapshot-6-structured.json", - "sessionId": "01KV851DZH93PX9FQA3C199TSS", - "capturedAtSeq": 6, - "createdAt": "2026-06-16T12:03:40.693Z", + "filename": "snapshot-5-structured.json", + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", + "capturedAtSeq": 5, + "createdAt": "2026-06-16T13:29:47.434Z", "metadata": { "format": "structured", "rendererBackend": "libghostty-vt", "cols": 80, "rows": 24, - "cursorRow": 5, + "cursorRow": 2, "cursorCol": 7 } }, { - "id": "01KV851RXQP4ZQRBXFNHHNSQTM", + "id": "01KV89ZEH0G4DE7PXFB84D7AYN", "kind": "screenshot", - "filename": "screenshot-6-reference-dark.png", - "sessionId": "01KV851DZH93PX9FQA3C199TSS", - "capturedAtSeq": 6, - "createdAt": "2026-06-16T12:03:42.647Z", - "sha256": "5864fb7aed3726a2a88ca79a7ce5db53159c35fcd0ca7982b3e15c367f173af7", + "filename": "screenshot-5-reference-dark.png", + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", + "capturedAtSeq": 5, + "createdAt": "2026-06-16T13:29:49.344Z", + "sha256": "d299aedfc3dbbd19a780700059ba49aeecf732a6131e8aff62f02675399d785c", "metadata": { "profileName": "reference-dark", "cols": 80, "rows": 24, - "pngSizeBytes": 13319, + "pngSizeBytes": 6810, "cursorVisible": false, "rendererBackend": "ghostty-web", "pixelWidth": 640, @@ -39,34 +39,34 @@ } }, { - "id": "01KV851TCV0Q3J1QS8KR93E6FS", + "id": "01KV89ZG1TQ0APYCT5N9PAZGRJ", "kind": "snapshot", - "filename": "snapshot-6-text.json", - "sessionId": "01KV851DZH93PX9FQA3C199TSS", - "capturedAtSeq": 6, - "createdAt": "2026-06-16T12:03:44.156Z", + "filename": "snapshot-5-text.json", + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", + "capturedAtSeq": 5, + "createdAt": "2026-06-16T13:29:50.906Z", "metadata": { "format": "text", "rendererBackend": "ghostty-web", "cols": 80, "rows": 24, - "cursorRow": 5, + "cursorRow": 2, "cursorCol": 7 } }, { - "id": "01KV851W9NV6PWW8TP0S8CH5XF", + "id": "01KV89ZHZE0SRFM00KCZP8D36Q", "kind": "screenshot", - "filename": "screenshot-6-reference-dark.png", - "sessionId": "01KV851DZH93PX9FQA3C199TSS", - "capturedAtSeq": 6, - "createdAt": "2026-06-16T12:03:46.101Z", - "sha256": "5864fb7aed3726a2a88ca79a7ce5db53159c35fcd0ca7982b3e15c367f173af7", + "filename": "screenshot-5-reference-dark.png", + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", + "capturedAtSeq": 5, + "createdAt": "2026-06-16T13:29:52.878Z", + "sha256": "d299aedfc3dbbd19a780700059ba49aeecf732a6131e8aff62f02675399d785c", "metadata": { "profileName": "reference-dark", "cols": 80, "rows": 24, - "pngSizeBytes": 13319, + "pngSizeBytes": 6810, "cursorVisible": false, "rendererBackend": "ghostty-web", "pixelWidth": 640, @@ -75,17 +75,17 @@ } }, { - "id": "01KV8524W53CY3E89V0RFESPH3", + "id": "01KV89ZTJ6HD0FK5AXT6730B7P", "kind": "video", - "filename": "recording-14-webm.webm", - "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "filename": "default-webm.webm", + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", "capturedAtSeq": 14, - "createdAt": "2026-06-16T12:03:54.886Z", - "sha256": "6c1ae7c446c1df85c85027ab74ecbd270e23398e5cf48cd91c73cd37b8bfb0eb", - "bytes": 49225, + "createdAt": "2026-06-16T13:30:01.670Z", + "sha256": "c84b8d29605eb19a546a18fec735c921e1882e7d0a569e22d09dcde24afc74f1", + "bytes": 26115, "metadata": { "format": "webm", - "outputPath": "/tmp/agent-tty-default-renderer.w26eUX/sessions/01KV851DZH93PX9FQA3C199TSS/artifacts/recording-14-webm.webm", + "outputPath": "/home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/videos/default-webm.webm", "width": 80, "height": 24, "profileName": "reference-dark", @@ -97,17 +97,17 @@ } }, { - "id": "01KV8529Z6X3AE5GTCFX277DRE", + "id": "01KV89ZZRFB9FEM6H378JEM9PF", "kind": "video", - "filename": "recording-14-webm.webm", - "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "filename": "explicit-libghostty-vt-webm.webm", + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", "capturedAtSeq": 14, - "createdAt": "2026-06-16T12:04:00.103Z", - "sha256": "26feddb53310eaf020503b6f75e73b96d731995d6fa104aa046601c3a94d0328", - "bytes": 49695, + "createdAt": "2026-06-16T13:30:06.992Z", + "sha256": "33b7fd9f3f8b00f95fd0cb26b011134a7fa405b3295035528316159b92ab7cf1", + "bytes": 27083, "metadata": { "format": "webm", - "outputPath": "/tmp/agent-tty-default-renderer.w26eUX/sessions/01KV851DZH93PX9FQA3C199TSS/artifacts/recording-14-webm.webm", + "outputPath": "/home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/videos/explicit-libghostty-vt-webm.webm", "width": 80, "height": 24, "profileName": "reference-dark", @@ -119,21 +119,21 @@ } }, { - "id": "01KV852BE8YJKSQSWSJX6R1AVH", + "id": "01KV8A016HZYKE7P6KK3EANNJ6", "kind": "recording", - "filename": "recording-14-asciicast.cast", - "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "filename": "default.cast", + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", "capturedAtSeq": 14, - "createdAt": "2026-06-16T12:04:01.609Z", - "sha256": "f2c09e7d3a884898e05ed1100b98c0a1063d75f0a38e9011acd51e5e2190d5dc", - "bytes": 667, + "createdAt": "2026-06-16T13:30:08.466Z", + "sha256": "20777abc4d55e0506cc685c13f08f31a376b8dd2124f6f7c3101b5a7945c5296", + "bytes": 468, "metadata": { "format": "asciicast", - "outputPath": "/tmp/agent-tty-default-renderer.w26eUX/sessions/01KV851DZH93PX9FQA3C199TSS/artifacts/recording-14-asciicast.cast", + "outputPath": "/home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/recordings/default.cast", "width": 80, "height": 24, - "title": "01KV851DZH93PX9FQA3C199TSS", - "timestamp": 1781611412, + "title": "01KV89Z29JPZD4EG29CHER1MN8", + "timestamp": 1781616577, "outputEventCount": 10, "resizeEventCount": 0, "markerCount": 0 diff --git a/dogfood/20260616-default-semantic-renderer/artifact-sha256.txt b/dogfood/20260616-default-semantic-renderer/artifact-sha256.txt index 4f97d918..c8bca000 100644 --- a/dogfood/20260616-default-semantic-renderer/artifact-sha256.txt +++ b/dogfood/20260616-default-semantic-renderer/artifact-sha256.txt @@ -1,5 +1,5 @@ -5864fb7aed3726a2a88ca79a7ce5db53159c35fcd0ca7982b3e15c367f173af7 /home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/screenshots/default-screenshot.png -5864fb7aed3726a2a88ca79a7ce5db53159c35fcd0ca7982b3e15c367f173af7 /home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/screenshots/explicit-libghostty-vt-screenshot.png -6c1ae7c446c1df85c85027ab74ecbd270e23398e5cf48cd91c73cd37b8bfb0eb /home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/videos/default-webm.webm -26feddb53310eaf020503b6f75e73b96d731995d6fa104aa046601c3a94d0328 /home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/videos/explicit-libghostty-vt-webm.webm -f2c09e7d3a884898e05ed1100b98c0a1063d75f0a38e9011acd51e5e2190d5dc /home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/recordings/default.cast +d299aedfc3dbbd19a780700059ba49aeecf732a6131e8aff62f02675399d785c screenshots/default-screenshot.png +d299aedfc3dbbd19a780700059ba49aeecf732a6131e8aff62f02675399d785c screenshots/explicit-libghostty-vt-screenshot.png +c84b8d29605eb19a546a18fec735c921e1882e7d0a569e22d09dcde24afc74f1 videos/default-webm.webm +33b7fd9f3f8b00f95fd0cb26b011134a7fa405b3295035528316159b92ab7cf1 videos/explicit-libghostty-vt-webm.webm +20777abc4d55e0506cc685c13f08f31a376b8dd2124f6f7c3101b5a7945c5296 recordings/default.cast diff --git a/dogfood/20260616-default-semantic-renderer/create.json b/dogfood/20260616-default-semantic-renderer/create.json index 74ab2fca..1ad2d586 100644 --- a/dogfood/20260616-default-semantic-renderer/create.json +++ b/dogfood/20260616-default-semantic-renderer/create.json @@ -1,10 +1,10 @@ { "ok": true, "command": "create", - "timestamp": "2026-06-16T12:03:32.281Z", + "timestamp": "2026-06-16T13:29:37.564Z", "result": { - "sessionId": "01KV851DZH93PX9FQA3C199TSS", - "createdAt": "2026-06-16T12:03:31.444Z", + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", + "createdAt": "2026-06-16T13:29:36.822Z", "cols": 80, "rows": 24, "shell": "/bin/bash" diff --git a/dogfood/20260616-default-semantic-renderer/default-cast.json b/dogfood/20260616-default-semantic-renderer/default-cast.json index 4665c13d..883f182d 100644 --- a/dogfood/20260616-default-semantic-renderer/default-cast.json +++ b/dogfood/20260616-default-semantic-renderer/default-cast.json @@ -1,20 +1,20 @@ { "ok": true, "command": "record export", - "timestamp": "2026-06-16T12:04:01.613Z", + "timestamp": "2026-06-16T13:30:08.471Z", "result": { - "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", "format": "asciicast", - "artifactPath": "/tmp/agent-tty-default-renderer.w26eUX/sessions/01KV851DZH93PX9FQA3C199TSS/artifacts/recording-14-asciicast.cast", - "bytes": 667, - "sha256": "f2c09e7d3a884898e05ed1100b98c0a1063d75f0a38e9011acd51e5e2190d5dc", + "artifactPath": "/home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/recordings/default.cast", + "bytes": 468, + "sha256": "20777abc4d55e0506cc685c13f08f31a376b8dd2124f6f7c3101b5a7945c5296", "capturedAtSeq": 14, - "durationMs": 16177, + "durationMs": 17608, "metadata": { "width": 80, "height": 24, - "title": "01KV851DZH93PX9FQA3C199TSS", - "timestamp": 1781611412, + "title": "01KV89Z29JPZD4EG29CHER1MN8", + "timestamp": 1781616577, "outputEventCount": 10, "resizeEventCount": 0, "markerCount": 0 diff --git a/dogfood/20260616-default-semantic-renderer/default-screenshot.json b/dogfood/20260616-default-semantic-renderer/default-screenshot.json index d2c52f83..893dda74 100644 --- a/dogfood/20260616-default-semantic-renderer/default-screenshot.json +++ b/dogfood/20260616-default-semantic-renderer/default-screenshot.json @@ -1,20 +1,20 @@ { "ok": true, "command": "screenshot", - "timestamp": "2026-06-16T12:03:42.650Z", + "timestamp": "2026-06-16T13:29:49.348Z", "result": { - "sessionId": "01KV851DZH93PX9FQA3C199TSS", - "capturedAtSeq": 6, + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", + "capturedAtSeq": 5, "profileName": "reference-dark", "cols": 80, "rows": 24, - "artifactPath": "/tmp/agent-tty-default-renderer.w26eUX/sessions/01KV851DZH93PX9FQA3C199TSS/artifacts/screenshot-6-reference-dark.png", - "pngSizeBytes": 13319, + "artifactPath": "/tmp/agent-tty-default-renderer.NIhHYj/sessions/01KV89Z29JPZD4EG29CHER1MN8/artifacts/screenshot-5-reference-dark.png", + "pngSizeBytes": 6810, "cursorVisible": false, "rendererBackend": "ghostty-web", "pixelWidth": 640, "pixelHeight": 384, - "sha256": "5864fb7aed3726a2a88ca79a7ce5db53159c35fcd0ca7982b3e15c367f173af7", + "sha256": "d299aedfc3dbbd19a780700059ba49aeecf732a6131e8aff62f02675399d785c", "renderProfileHash": "8ffed6af301ec7c0e6b69599c3be0d1d12096f9fcdfc59d0bbb4cc474d64c53d" } } diff --git a/dogfood/20260616-default-semantic-renderer/default-snapshot-artifact.json b/dogfood/20260616-default-semantic-renderer/default-snapshot-artifact.json index 8af3a90b..c836945f 100644 --- a/dogfood/20260616-default-semantic-renderer/default-snapshot-artifact.json +++ b/dogfood/20260616-default-semantic-renderer/default-snapshot-artifact.json @@ -1,16 +1,16 @@ { - "id": "01KV851Q0MS2YQW4MTWXYNJBBD", + "id": "01KV89ZCN99JFHWA7DZ0FDQV3V", "kind": "snapshot", - "filename": "snapshot-6-structured.json", - "sessionId": "01KV851DZH93PX9FQA3C199TSS", - "capturedAtSeq": 6, - "createdAt": "2026-06-16T12:03:40.693Z", + "filename": "snapshot-5-structured.json", + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", + "capturedAtSeq": 5, + "createdAt": "2026-06-16T13:29:47.434Z", "metadata": { "format": "structured", "rendererBackend": "libghostty-vt", "cols": 80, "rows": 24, - "cursorRow": 5, + "cursorRow": 2, "cursorCol": 7 } } diff --git a/dogfood/20260616-default-semantic-renderer/default-snapshot.json b/dogfood/20260616-default-semantic-renderer/default-snapshot.json index 64e2b57a..1cb34b9d 100644 --- a/dogfood/20260616-default-semantic-renderer/default-snapshot.json +++ b/dogfood/20260616-default-semantic-renderer/default-snapshot.json @@ -1,40 +1,40 @@ { "ok": true, "command": "snapshot", - "timestamp": "2026-06-16T12:03:40.697Z", + "timestamp": "2026-06-16T13:29:47.438Z", "result": { "format": "structured", - "sessionId": "01KV851DZH93PX9FQA3C199TSS", - "capturedAtSeq": 6, + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", + "capturedAtSeq": 5, "cols": 80, "rows": 24, - "cursorRow": 5, + "cursorRow": 2, "cursorCol": 7, "isAltScreen": false, "visibleLines": [ { "row": 0, - "text": "READY> (node:2465832) [DEP0205] DeprecationWarning: `module.register()` is depre" + "text": "READY> Default semantic renderer proof" }, { "row": 1, - "text": "cated. Use `module.registerHooks()` instead." + "text": "ECHO: Default semantic renderer proof" }, { "row": 2, - "text": "(Use `node --trace-deprecation ...` to show where the warning was created)" + "text": "READY>" }, { "row": 3, - "text": "Default semantic renderer proof" + "text": "" }, { "row": 4, - "text": "ECHO: Default semantic renderer proof" + "text": "" }, { "row": 5, - "text": "READY>" + "text": "" }, { "row": 6, @@ -109,6 +109,6 @@ "text": "" } ], - "screenHash": "0feb80f00d88c52b4a000f6b1f718a63c6136bf8c9c2d90cc85c123cdf36f229" + "screenHash": "63f713a31c9ee5a4c004fba7983dab96ef5352804cd0c2de408f5c3570ea03a5" } } diff --git a/dogfood/20260616-default-semantic-renderer/default-wait-inspect.json b/dogfood/20260616-default-semantic-renderer/default-wait-inspect.json new file mode 100644 index 00000000..4db38f38 --- /dev/null +++ b/dogfood/20260616-default-semantic-renderer/default-wait-inspect.json @@ -0,0 +1,51 @@ +{ + "ok": true, + "command": "inspect", + "timestamp": "2026-06-16T13:29:44.276Z", + "result": { + "session": { + "version": 1, + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", + "createdAt": "2026-06-16T13:29:36.822Z", + "updatedAt": "2026-06-16T13:29:37.550Z", + "status": "running", + "command": ["npx", "tsx", "test/fixtures/apps/hello-prompt/main.ts"], + "cwd": "/home/coder/.mux/src/agent-terminal/vt-impl-5ds6", + "name": "default-semantic-renderer", + "shell": "/bin/bash", + "term": "xterm-256color", + "cols": 80, + "rows": 24, + "creationCols": 80, + "creationRows": 24, + "hostPid": 3127342, + "childPid": 3127397, + "exitCode": null, + "exitSignal": null + }, + "eventCount": 6, + "uptime": 7453, + "lastEventSeq": 5, + "terminationCategory": "running", + "artifacts": { + "total": 0, + "byKind": {}, + "missingCount": 0, + "health": "no-artifacts" + }, + "usedOfflineReplay": false, + "rendererRuntime": { + "backend": "libghostty-vt", + "mode": "live-host", + "status": "healthy", + "profile": "reference-dark", + "booted": true, + "bootInFlight": false + }, + "host": { + "cliVersion": "0.4.3", + "rpcSocketPath": "/tmp/agent-tty/55fb6b14/40123e969ae3" + }, + "eventLogBytes": 616 + } +} diff --git a/dogfood/20260616-default-semantic-renderer/default-wait.json b/dogfood/20260616-default-semantic-renderer/default-wait.json index 1d0fd6e9..f2999a10 100644 --- a/dogfood/20260616-default-semantic-renderer/default-wait.json +++ b/dogfood/20260616-default-semantic-renderer/default-wait.json @@ -1,14 +1,14 @@ { "ok": true, "command": "wait", - "timestamp": "2026-06-16T12:03:37.563Z", + "timestamp": "2026-06-16T13:29:42.928Z", "result": { "matched": true, "timedOut": false, "matchedText": "ECHO: Default semantic renderer proof", - "cursorRow": 5, + "cursorRow": 2, "cursorCol": 7, - "capturedAtSeq": 6, - "screenHash": "0feb80f00d88c52b4a000f6b1f718a63c6136bf8c9c2d90cc85c123cdf36f229" + "capturedAtSeq": 5, + "screenHash": "63f713a31c9ee5a4c004fba7983dab96ef5352804cd0c2de408f5c3570ea03a5" } } diff --git a/dogfood/20260616-default-semantic-renderer/default-webm.json b/dogfood/20260616-default-semantic-renderer/default-webm.json index 8678876d..c8e5e05a 100644 --- a/dogfood/20260616-default-semantic-renderer/default-webm.json +++ b/dogfood/20260616-default-semantic-renderer/default-webm.json @@ -1,15 +1,15 @@ { "ok": true, "command": "record export", - "timestamp": "2026-06-16T12:03:54.892Z", + "timestamp": "2026-06-16T13:30:01.673Z", "result": { - "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", "format": "webm", - "artifactPath": "/tmp/agent-tty-default-renderer.w26eUX/sessions/01KV851DZH93PX9FQA3C199TSS/artifacts/recording-14-webm.webm", - "bytes": 49225, - "sha256": "6c1ae7c446c1df85c85027ab74ecbd270e23398e5cf48cd91c73cd37b8bfb0eb", + "artifactPath": "/home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/videos/default-webm.webm", + "bytes": 26115, + "sha256": "c84b8d29605eb19a546a18fec735c921e1882e7d0a569e22d09dcde24afc74f1", "capturedAtSeq": 14, - "durationMs": 16177, + "durationMs": 17608, "metadata": { "width": 80, "height": 24, diff --git a/dogfood/20260616-default-semantic-renderer/doctor.json b/dogfood/20260616-default-semantic-renderer/doctor.json index ab2be5cb..7fd64cec 100644 --- a/dogfood/20260616-default-semantic-renderer/doctor.json +++ b/dogfood/20260616-default-semantic-renderer/doctor.json @@ -1,7 +1,7 @@ { "ok": true, "command": "doctor", - "timestamp": "2026-06-16T12:03:30.112Z", + "timestamp": "2026-06-16T13:29:35.518Z", "result": { "ok": true, "checks": { @@ -27,37 +27,37 @@ { "name": "home_isolation", "status": "pass", - "message": "agent-tty home is isolated from system home: /tmp/agent-tty-default-renderer.w26eUX", - "durationMs": 1 + "message": "agent-tty home is isolated from system home: /tmp/agent-tty-default-renderer.NIhHYj", + "durationMs": 0 }, { "name": "home-writable", "status": "pass", - "message": "home writable: /tmp/agent-tty-default-renderer.w26eUX", + "message": "home writable: /tmp/agent-tty-default-renderer.NIhHYj", "durationMs": 1 }, { "name": "pty-spawn", "status": "pass", "message": "spawned /home/coder/.local/share/mise/installs/node/26.2.0/bin/node", - "durationMs": 37 + "durationMs": 32 }, { "name": "socket-viable", "status": "pass", - "message": "socket ok: /tmp/agent-tty/9cf86873/3806c31448a6", - "durationMs": 2 + "message": "socket ok: /tmp/agent-tty/55fb6b14/95689a30d574", + "durationMs": 3 }, { "name": "artifact-atomicity", "status": "pass", - "message": "atomic rename ok: /tmp/agent-tty-default-renderer.w26eUX/sessions/doctor-2464905-mqglflt3-3/artifacts", - "durationMs": 2 + "message": "atomic rename ok: /tmp/agent-tty-default-renderer.NIhHYj/sessions/doctor-3126662-mqgoibi9-3/artifacts", + "durationMs": 1 }, { "name": "event-log-writable", "status": "pass", - "message": "append ok: /tmp/agent-tty-default-renderer.w26eUX/sessions/doctor-2464905-mqglflt4-5/events.jsonl", + "message": "append ok: /tmp/agent-tty-default-renderer.NIhHYj/sessions/doctor-3126662-mqgoibia-5/events.jsonl", "durationMs": 1 } ], @@ -78,19 +78,19 @@ "name": "browser_launch", "status": "pass", "message": "chromium launches", - "durationMs": 107 + "durationMs": 109 }, { "name": "ghostty_web_available", "status": "pass", "message": "WASM available", - "durationMs": 131 + "durationMs": 82 }, { "name": "screenshot_viable", "status": "pass", "message": "viable", - "durationMs": 165 + "durationMs": 153 }, { "name": "libghostty_vt_available", diff --git a/dogfood/20260616-default-semantic-renderer/environment.txt b/dogfood/20260616-default-semantic-renderer/environment.txt index 9d1f161d..e4135aa7 100644 --- a/dogfood/20260616-default-semantic-renderer/environment.txt +++ b/dogfood/20260616-default-semantic-renderer/environment.txt @@ -5,10 +5,16 @@ $ npm --version 11.13.0 $ git rev-parse HEAD -d27bb8d34e97e54019bb8f7b4dd04195ca20718c +5b13076afa422ec9b6a0c66c20b21fd4d24d181e $ git log --oneline -n 1 -d27bb8d fix: sync README version in release PRs (#156) +5b13076 fix: address renderer default review findings + +$ git status --short (before capture) +clean + +$ git diff --binary | sha256sum (before capture) +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 $ uname -a Linux aaaaaaa 6.8.0-124-generic #124-Ubuntu SMP PREEMPT_DYNAMIC Tue May 26 13:00:45 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux @@ -20,7 +26,7 @@ $ npx tsx src/cli/main.ts version --json { "ok": true, "command": "version", - "timestamp": "2026-06-16T12:03:25.596Z", + "timestamp": "2026-06-16T13:29:31.103Z", "result": { "cliVersion": "0.4.3", "protocolVersion": "0.1.0", @@ -66,7 +72,7 @@ $ npx tsx src/cli/main.ts doctor --json { "ok": true, "command": "doctor", - "timestamp": "2026-06-16T12:03:27.189Z", + "timestamp": "2026-06-16T13:29:32.658Z", "result": { "ok": true, "checks": { @@ -92,37 +98,37 @@ $ npx tsx src/cli/main.ts doctor --json { "name": "home_isolation", "status": "pass", - "message": "agent-tty home is isolated from system home: /tmp/agent-tty-default-renderer.w26eUX", - "durationMs": 0 + "message": "agent-tty home is isolated from system home: /tmp/agent-tty-default-renderer.NIhHYj", + "durationMs": 1 }, { "name": "home-writable", "status": "pass", - "message": "home writable: /tmp/agent-tty-default-renderer.w26eUX", - "durationMs": 1 + "message": "home writable: /tmp/agent-tty-default-renderer.NIhHYj", + "durationMs": 3 }, { "name": "pty-spawn", "status": "pass", "message": "spawned /home/coder/.local/share/mise/installs/node/26.2.0/bin/node", - "durationMs": 31 + "durationMs": 34 }, { "name": "socket-viable", "status": "pass", - "message": "socket ok: /tmp/agent-tty/9cf86873/8ea9a600aad8", - "durationMs": 3 + "message": "socket ok: /tmp/agent-tty/55fb6b14/c9e2d8289d92", + "durationMs": 4 }, { "name": "artifact-atomicity", "status": "pass", - "message": "atomic rename ok: /tmp/agent-tty-default-renderer.w26eUX/sessions/doctor-2464044-mqglfjkn-3/artifacts", - "durationMs": 1 + "message": "atomic rename ok: /tmp/agent-tty-default-renderer.NIhHYj/sessions/doctor-3125475-mqgoi9ac-3/artifacts", + "durationMs": 2 }, { "name": "event-log-writable", "status": "pass", - "message": "append ok: /tmp/agent-tty-default-renderer.w26eUX/sessions/doctor-2464044-mqglfjko-5/events.jsonl", + "message": "append ok: /tmp/agent-tty-default-renderer.NIhHYj/sessions/doctor-3125475-mqgoi9ae-5/events.jsonl", "durationMs": 1 } ], @@ -143,19 +149,19 @@ $ npx tsx src/cli/main.ts doctor --json "name": "browser_launch", "status": "pass", "message": "chromium launches", - "durationMs": 108 + "durationMs": 103 }, { "name": "ghostty_web_available", "status": "pass", "message": "WASM available", - "durationMs": 112 + "durationMs": 81 }, { "name": "screenshot_viable", "status": "pass", "message": "viable", - "durationMs": 157 + "durationMs": 176 }, { "name": "libghostty_vt_available", diff --git a/dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot-artifact.json b/dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot-artifact.json index a789b807..2ded7b6f 100644 --- a/dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot-artifact.json +++ b/dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot-artifact.json @@ -1,16 +1,16 @@ { - "id": "01KV851TCV0Q3J1QS8KR93E6FS", + "id": "01KV89ZG1TQ0APYCT5N9PAZGRJ", "kind": "snapshot", - "filename": "snapshot-6-text.json", - "sessionId": "01KV851DZH93PX9FQA3C199TSS", - "capturedAtSeq": 6, - "createdAt": "2026-06-16T12:03:44.156Z", + "filename": "snapshot-5-text.json", + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", + "capturedAtSeq": 5, + "createdAt": "2026-06-16T13:29:50.906Z", "metadata": { "format": "text", "rendererBackend": "ghostty-web", "cols": 80, "rows": 24, - "cursorRow": 5, + "cursorRow": 2, "cursorCol": 7 } } diff --git a/dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot.json b/dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot.json index 1b989b3c..e3552bf3 100644 --- a/dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot.json +++ b/dogfood/20260616-default-semantic-renderer/explicit-ghostty-web-snapshot.json @@ -1,16 +1,16 @@ { "ok": true, "command": "snapshot", - "timestamp": "2026-06-16T12:03:44.160Z", + "timestamp": "2026-06-16T13:29:50.910Z", "result": { "format": "text", - "sessionId": "01KV851DZH93PX9FQA3C199TSS", - "capturedAtSeq": 6, + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", + "capturedAtSeq": 5, "cols": 80, "rows": 24, - "cursorRow": 5, + "cursorRow": 2, "cursorCol": 7, - "text": "READY> (node:2465832) [DEP0205] DeprecationWarning: `module.register()` is depre\ncated. Use `module.registerHooks()` instead.\n(Use `node --trace-deprecation ...` to show where the warning was created)\nDefault semantic renderer proof\nECHO: Default semantic renderer proof\nREADY>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", - "screenHash": "0feb80f00d88c52b4a000f6b1f718a63c6136bf8c9c2d90cc85c123cdf36f229" + "text": "READY> Default semantic renderer proof\nECHO: Default semantic renderer proof\nREADY>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "screenHash": "63f713a31c9ee5a4c004fba7983dab96ef5352804cd0c2de408f5c3570ea03a5" } } diff --git a/dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-screenshot.json b/dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-screenshot.json index 8a940b08..412dca82 100644 --- a/dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-screenshot.json +++ b/dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-screenshot.json @@ -1,20 +1,20 @@ { "ok": true, "command": "screenshot", - "timestamp": "2026-06-16T12:03:46.105Z", + "timestamp": "2026-06-16T13:29:52.882Z", "result": { - "sessionId": "01KV851DZH93PX9FQA3C199TSS", - "capturedAtSeq": 6, + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", + "capturedAtSeq": 5, "profileName": "reference-dark", "cols": 80, "rows": 24, - "artifactPath": "/tmp/agent-tty-default-renderer.w26eUX/sessions/01KV851DZH93PX9FQA3C199TSS/artifacts/screenshot-6-reference-dark.png", - "pngSizeBytes": 13319, + "artifactPath": "/tmp/agent-tty-default-renderer.NIhHYj/sessions/01KV89Z29JPZD4EG29CHER1MN8/artifacts/screenshot-5-reference-dark.png", + "pngSizeBytes": 6810, "cursorVisible": false, "rendererBackend": "ghostty-web", "pixelWidth": 640, "pixelHeight": 384, - "sha256": "5864fb7aed3726a2a88ca79a7ce5db53159c35fcd0ca7982b3e15c367f173af7", + "sha256": "d299aedfc3dbbd19a780700059ba49aeecf732a6131e8aff62f02675399d785c", "renderProfileHash": "8ffed6af301ec7c0e6b69599c3be0d1d12096f9fcdfc59d0bbb4cc474d64c53d" } } diff --git a/dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-webm.json b/dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-webm.json index 72eecce4..36053c2f 100644 --- a/dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-webm.json +++ b/dogfood/20260616-default-semantic-renderer/explicit-libghostty-vt-webm.json @@ -1,15 +1,15 @@ { "ok": true, "command": "record export", - "timestamp": "2026-06-16T12:04:00.110Z", + "timestamp": "2026-06-16T13:30:06.995Z", "result": { - "sessionId": "01KV851DZH93PX9FQA3C199TSS", + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", "format": "webm", - "artifactPath": "/tmp/agent-tty-default-renderer.w26eUX/sessions/01KV851DZH93PX9FQA3C199TSS/artifacts/recording-14-webm.webm", - "bytes": 49695, - "sha256": "26feddb53310eaf020503b6f75e73b96d731995d6fa104aa046601c3a94d0328", + "artifactPath": "/home/coder/.mux/src/agent-terminal/vt-impl-5ds6/dogfood/20260616-default-semantic-renderer/videos/explicit-libghostty-vt-webm.webm", + "bytes": 27083, + "sha256": "33b7fd9f3f8b00f95fd0cb26b011134a7fa405b3295035528316159b92ab7cf1", "capturedAtSeq": 14, - "durationMs": 16177, + "durationMs": 17608, "metadata": { "width": 80, "height": 24, diff --git a/dogfood/20260616-default-semantic-renderer/inspect.json b/dogfood/20260616-default-semantic-renderer/inspect.json index ad6c7153..e4ed7312 100644 --- a/dogfood/20260616-default-semantic-renderer/inspect.json +++ b/dogfood/20260616-default-semantic-renderer/inspect.json @@ -1,13 +1,13 @@ { "ok": true, "command": "inspect", - "timestamp": "2026-06-16T12:04:02.980Z", + "timestamp": "2026-06-16T13:30:09.883Z", "result": { "session": { "version": 1, - "sessionId": "01KV851DZH93PX9FQA3C199TSS", - "createdAt": "2026-06-16T12:03:31.444Z", - "updatedAt": "2026-06-16T12:03:48.740Z", + "sessionId": "01KV89Z29JPZD4EG29CHER1MN8", + "createdAt": "2026-06-16T13:29:36.822Z", + "updatedAt": "2026-06-16T13:29:55.519Z", "status": "exited", "command": ["npx", "tsx", "test/fixtures/apps/hello-prompt/main.ts"], "cwd": "/home/coder/.mux/src/agent-terminal/vt-impl-5ds6", @@ -18,13 +18,13 @@ "rows": 24, "creationCols": 80, "creationRows": 24, - "hostPid": 2465597, - "childPid": 2465610, + "hostPid": 3127342, + "childPid": 3127397, "exitCode": 0, "exitSignal": null }, "eventCount": 15, - "uptime": 17296, + "uptime": 18697, "lastEventSeq": 14, "terminationCategory": "clean-exit", "artifacts": { @@ -35,8 +35,25 @@ "video": 2, "recording": 1 }, - "missingCount": 0, - "health": "healthy" + "missingCount": 3, + "health": "missing-artifacts", + "missing": [ + { + "id": "01KV89ZTJ6HD0FK5AXT6730B7P", + "kind": "video", + "filename": "default-webm.webm" + }, + { + "id": "01KV89ZZRFB9FEM6H378JEM9PF", + "kind": "video", + "filename": "explicit-libghostty-vt-webm.webm" + }, + { + "id": "01KV8A016HZYKE7P6KK3EANNJ6", + "kind": "recording", + "filename": "default.cast" + } + ] }, "usedOfflineReplay": false, "rendererRuntime": { @@ -45,6 +62,6 @@ "status": "fallback", "reason": "session-not-running" }, - "eventLogBytes": 1613 + "eventLogBytes": 1414 } } diff --git a/dogfood/20260616-default-semantic-renderer/recordings/default.cast b/dogfood/20260616-default-semantic-renderer/recordings/default.cast index 48498819..b6660ab6 100644 --- a/dogfood/20260616-default-semantic-renderer/recordings/default.cast +++ b/dogfood/20260616-default-semantic-renderer/recordings/default.cast @@ -1,11 +1,11 @@ -{"version":2,"width":80,"height":24,"timestamp":1781611412,"title":"01KV851DZH93PX9FQA3C199TSS","sessionId":"01KV851DZH93PX9FQA3C199TSS","env":{"TERM":"xterm-256color"},"toolVersion":"0.4.3"} +{"version":2,"width":80,"height":24,"timestamp":1781616577,"title":"01KV89Z29JPZD4EG29CHER1MN8","sessionId":"01KV89Z29JPZD4EG29CHER1MN8","env":{"TERM":"xterm-256color"},"toolVersion":"0.4.3"} [0,"o","READY> "] -[0.001,"o","(node:2465832) [DEP0205] DeprecationWarning: `module.register()` is deprecated. Use `module.registerHooks()` instead.\r\n(Use `node --trace-deprecation ...` to show where the warning was created)\r\n"] -[2.492,"o","Default semantic renderer proof"] -[3.655,"o","\r\n"] -[3.655,"o","ECHO: Default semantic renderer proof\r\nREADY> "] -[14.993,"o","exit"] -[16.147,"o","\r\n"] -[16.147,"o","BYE\r\n"] -[16.166,"o","\\"] -[16.167,"o","\u001b[1G\u001b[0K"] +[2.46,"o","Default semantic renderer proof"] +[3.607,"o","\r\n"] +[3.608,"o","ECHO: Default semantic renderer proof\r\nREADY> "] +[16.419,"o","exit"] +[17.586,"o","\r\n"] +[17.587,"o","BYE\r\n"] +[17.597,"o","\\"] +[17.598,"o","\u001b[1G"] +[17.598,"o","\u001b[0K"] diff --git a/dogfood/20260616-default-semantic-renderer/screenshots/default-screenshot.png b/dogfood/20260616-default-semantic-renderer/screenshots/default-screenshot.png index 7627a36802a553b06150f6632c2b0dc77dd823c1..410439f32e8fda5bccaab1c12199811007ce86ca 100644 GIT binary patch literal 6810 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 literal 13319 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 O;D)KywW_Ok{`^0}Vh;8I diff --git a/dogfood/20260616-default-semantic-renderer/screenshots/explicit-libghostty-vt-screenshot.png b/dogfood/20260616-default-semantic-renderer/screenshots/explicit-libghostty-vt-screenshot.png index 7627a36802a553b06150f6632c2b0dc77dd823c1..410439f32e8fda5bccaab1c12199811007ce86ca 100644 GIT binary patch literal 6810 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 literal 13319 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 O;D)KywW_Ok{`^0}Vh;8I diff --git a/dogfood/20260616-default-semantic-renderer/version.json b/dogfood/20260616-default-semantic-renderer/version.json index 9c89a5a9..70e48355 100644 --- a/dogfood/20260616-default-semantic-renderer/version.json +++ b/dogfood/20260616-default-semantic-renderer/version.json @@ -1,7 +1,7 @@ { "ok": true, "command": "version", - "timestamp": "2026-06-16T12:03:28.301Z", + "timestamp": "2026-06-16T13:29:33.794Z", "result": { "cliVersion": "0.4.3", "protocolVersion": "0.1.0", diff --git a/dogfood/20260616-default-semantic-renderer/videos/default-webm.webm b/dogfood/20260616-default-semantic-renderer/videos/default-webm.webm index 8e301290f5d4a61c09e5fe90d84e12bdb56e520e..4eeb9a0389dbfc5f6474dd8b4834317799e0d74b 100644 GIT binary patch literal 26115 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 S0HvXrpL<#L2W71wApZ*{dlEeW literal 49225 zcmeFZ1#}$Ck|x|@W@csvi$3nVG?2X0VvCSNGnxGw65gf3gz` z1%itO1Hp0C11!M+e^3`5nqr1)i(+e}dihr@*-{Jj3Siz}DQt~OoA=bbVr$fU9=b-a zrFumm7~NkEy>KO^` zc!Gdn6gxp*!9VE%Hj}FZ!5>-z!Ew`p;F{H8nvL&=CLs792t2vC{RRR60aLnwEHL^9a9nUeC~R}@l*e*eGy{~_>yDFRJ^x3{4i z(5^e`S}1dhR+H*O!4eQa3z&ulAfOooZ~?duH;6bl=m4B~geQnQUF_R49Vts~-U*k? zgMT|PLO%Of`YrNSIl4fe%s6M|O#&zw1Oik5aWMbF`rrn<4JtGR#NB@Gy?+01250lI zVSh)VtZyd_fPx_);LBf(4E~hyPeuVS$^SNwKbbF5Nc;oAzMXKS1{6F30l9!0q<4`h z|HcUb@cifC7;pK)?_H4HJNXdh>T>hur-;Wj7Ibv25Of z`NLErJO~CDb^iu`Lovp=@CI}SEkeEgr({Afrnh!-q5=wDfq)CZ^gC3jWB&p=0O}Ct z-@&RCrW$JXpV=T-FHpqXTt`p;C>{*=ui{hx6)gbN`M*#o1^+IL(0AMery zz&25@JWi7nsD`~ahD2gysQDb2(5EoU==Q$Fm*6t z2bZnPC7gNzZ(;uCFF&@xd~SQ%15j4h?z5YWQ8k2fjlf^QA5dYckwttPTmO#g1Kyr# z{GUi}_&w_BBxtAdO3oobDEu5q_BxEjlx_>YAW%qWDF}+?2WYe7mNhO!D#uYlZ$VMU z9k-18;58WFZ9O%&>H*r{E~<0yoUkbj%|EVH|LQG0)*BZB?nQ# zR!dbIXI7OOKRxO)5N`1XR9LFqJt`k#r|RzG$0d)Wn1fnL7fQdVFI zR1IXy7IwVwK-neXJm)43E$dKut%0tod%eK*7#d+|&E4evYbJXe)PK5f)7y0Y2#G%w zR&!*(jB}ic_d~B<`-TfCyc@3}qN%N!oCnkY_=)-9^Sm#er^YGbNGXjbsL7LcCMvcX z?Dw#R4FBzw3kY;*X+CbmAbfs zkn)@46Yz!N9<*hd>mTu2{2Bm)eYy_|r^}NLEONhkdqutFFEs3rCT(bX44L zoUxpAR{FQ+?EBOC?!0(k(F{scfV^I$?ol447lqP5xqkP4)UWDKCCkF6Fx79pois0n zZywKJcL)!b7Y@fH37vDDKEjA~z)gC9Kh>M^8_x6nga1?K?>8S`;b)R5HvWD>dhHtsv;j&YX~+S+j)QdkcR>oTpb&+? z?tnAEI$9Tnxx@|(2QzJ2t`XW2duyZzLl}+9WcTj)ujVzCALQ#c-Oik_!fN!dJ4uFW z%~|DpayqsmBt{`_fssu!-ni<-Kp$yy8BO=kxxA)6F@Ge)5&b@}`uzZZh$)y>SzzT= zMp#ni>9Mr?FeBb7I4VHbQegLl3CpM~_Tob_&=6uCw)e(n1xRMW<@znFhgI@8$G5$WE1FfhLNmet4~ zz4*PditizZuxOl?=ZHYN#+*IJc9BA4df4kLN&IS-{LP@(D^? zSmE@!ws;I~(~}3Rt`<3BU?tr5lQfI2;Zmmseop<52$TaVATz}c19;;t3o!Cwz|Ji^ zI7b0Dl*+NNMItkwVj&VO!U|gna>jR`?9qju9mhOVm(L@$+cBww;g>M>pbV+Va-h&5C;IaCakQp7Kglu!I#yomA*nqBgwAG(w#j2-YDv$L(x)cDCmnipL_A_BL}bckI0SaZx(lwnc6 z2_ofwIW1i42YFZ1?lF9n^gIK~^ws8Lgb2fu^@A3jh?kv$(>>^g{_Vauq@BgmL6bdW zsQ~7cldYa`ptZcmp%nhkwT{AkLsv;)PtlxiKlw}X0q^>`3~TwcRq!5MqrJDT%E&Zy z$(Fby`|FIMnL`jAmVj-B1tjc?CrU92pH77vV%OfdnCsZ{8Mzh1EVZ-n3-iGx)|}I^-kYdPqr`WRp$#DDaV2TE?f45ydFndw17w!!ilf-O+w#dl)bt+6!cekn9=y zr@wl3^`efoUt~^@3P>j1$d#d*p_WK8^g|=EF-OABgPM%Jr4Ua>$IghvvIn=)V1vcBTM7zYhWUOiawA$UvD&*LL;OWsFxb1(tL#-Sz7Ga*qSn# zC6?UAzz8dktU@a_eso?)HJdS0ICQn+gL3Qel}SvRKmWG4VtA1rztbV39U;DR z)LbFmrg@(ySNpAuP;mwd3_@Zz89b2oQpz&1rjb`oHCYWcGiUJUh7`vLTC5M3a;W?9 zeW%5kp`l&0NL+t9mqM15Jd0eHJZpKVr!%LpRZ{a8`!xxBhK!1_qoGhL)3_~)GU~h} zI)$}oW=HSD`S#^)6T#7>czg)|H+fCUx9u-@Uh)Wb;`as(g!!xBV%5gwYzF1EgDEA{ z)Ze>!AzhE?2tFd$hu4U%6SpJpO4|Cf%+Em^hR41&hZg1fl$fncG{}w`u(~8fvegAu zm3?@)r0;-zZvFwTQy}So@f`!abE7HIT1}%xueM{XcOso9JncZW{{-!=Dxg!Z9WuMD zf6N$h;f&r~1;|xlf)#Ip$2?n13>^h9+Vy=#yUW;2LrbU+mS!+zXdkK`t^pgTlFx6Xr1qGTALU=3IXa8ibNp)jvGqLG-yiewFe-cAKOBlv&U!j2m zye^*`fT^zP1dBukx$;dJcg7}wno2R%>E?0K7vKx;5ek7xzd~~JO8aT%iivdLB>*T( zL8x)th#nIpka~_W8t@Pe(~6h6t$C&9H3{u4v3TH^2qa&r*&V}AEL@Q3(Fn!zOR1#l zdncPn4O3)#q@*h3?SF&?e4|3Q8Deq}rRlC>C**eIhF19YRoZ7IK6+Y)zMVbKov=Bh zltGF~NkgWgT_Qtyjo!!~)FE)j{4vjCA@}Jf44LfN##LbNsU$7CO)d~EyCpf|&6S?y z>z~?u95c<_kW?`a!$(few8<-(LoO3y#7+5fv=+v>jVo_*_;n8JiNiy!(ga^k0J=YY z<_9{sEfpHr>SpYo0l_3gJMFnhc8UgFw10CNE_(8 zrE||SwgeEt$Jy{G`ssy+*S3||%2QSLLl>^Fvs3P6_FsMrHhk1j&ai*R?v}6AHLO9^ zC*$b8u+~ZPsgG^xUFW-#?$fO2o}7aysz=g>t`J(lo#$|gD_QK^s$Kgf`q&am9*~El3)(GxWgQcwpb@7$F?=H|^&d|E&w6uTes~qx6BC}>?^dvPA0Wzre zZoP$_aJQ^i=NUqO1WfzhZa#RP_;TT=5JfzTpMeNL{sUF`0a60#??E$EmK`y)1OwK1 z7E2$-wZnFWHSF?Ca85Wv+$%4=lo#;iIXssTN3EwdRXLpUIk$&pLW8j3m5P}#khq>% zY1o5sby~Ryt3Cdf^hDjySSa#OMAz_=eGegH9YC-D-a}uCexJK2Sa`W~LKF zK-6qz)t7rI+}9w^#+~PAh87|TA1t6RFfikN9%H>(4&reU^R|f2AQV%V`)sG5`V)1` zXYn%r8ATh6xo&WSX-V7RYi%(F!@lf~b%&E7o$?!1@D_TR7y|)t-=i~HL0-%0w)JAk zdgrkU#kQ*rv1*2vbzBKTIl;n<2h+&{&2#09E9ak0r^y0Z`D1M^o-FHWWj!jAyuS8P znCR7!Z^)WJ&o6fVlw;uwtiHkcLx?CJilqA9Cdm$GM0k~X>p2-Dz7)+mAAe=N>db2@ zoeIh{d}d;b@zHlpt`?uP+Ld(|Jz-~HcnQvxqj@$sP%Ch@K5rgII7YOem9CS3`$kn2Rf5mzx7J?O$?#QD$fpjthQgYc1^; zWR9b!>qb@U<5rv^Vgfflgq`f#teS8pSi;(_%Cq>uA0p-*0GExmTiWl4Aw5R_>I%d` z@^Pz^7=udZ|CtUl_tJykI}m>}0F4&Je#LefS1?Jlva9SJe9H-nkwM0oVt?b?&PBGhX- zV1vJiYx%e1VEh`n;-5nlXs^N4i#*fd9nA)0p%O6E^F2#rz>$e#`7l*07ZT z5Y!W=Hrbd`TS>`GgnREnQCzi#LNzqMV<>dq>`me&<$@)2SkEnR0G~H+(d8f!J@GF- zu@$5YG!nPNKAn;ryA!mKDV+=i@U82b_c|^v8c&&f8`s9_&){F+DtP=DX&Mj6U~vK4 ztSEWv6+QETWSc>D5E=D~@vTOIWK`mb_*695N>>B7?*nap?Tj7vwgHk@|44+Tt)2<&EQ2SGkCr1l z_LjgMEMribw^$eXUbszvNl1Q1GK_dd7mUB?VvzMtsBTp^TS^ zmsr@>l(WD0L~hJZTw2vFdGziRe>opz`%0nPC{!vsN6eo%QpdB^Vit6Bo$F)$5^5h1 zxU+F}VrlNsViL-GA$z@0&@j`7ZR==<0hSyCp}pXI8l2#wB;DbwMI5}_D2O#05swMq zsOU1eR)qrftXbg7eSqYJ`BX7YuAix83|X$9kmWw3FNAW~B;9TMF?dGYdBaF1vppr8 zI!JkofYf~hZ#2_tR{REkh}RnMO2a%gBc0%oxe~H&cGetloxiHA_7P@8M?q+Q;10+H zw*R?lIqwW{>&KFCS$29^T*RD9zd7dxg7Zb%@%bH zoDvs!XH4cj`qr4~gXTv;3mH9BB5#Aug3WF8_6OW{f`eR?s-u~Gn_FoXe>#vM)6}SN zUF*cuk?u#vkgagXqJt>`p05hTp)1HtJT^@MhSP+rkPC<}c_oT?>?yk!h&l?fqZ9ka zv)j`jjm_N#5)QqAkeL!Azw!oRMjblj$tC!R;e{vc43$h+Q^`wEg-+MlJitaen{p{?0yMnNG z)dk%BUgE6C>fFgM^JAY|W-1!I`QzsaC>LMMF&e4XtzeM?X8pgEb!GYI-{$DT9Y<^J z%SP0}bvRiDt*s5XnGzVwoEnncf5K5&_3sz3)n_X-swvStC6TF$Wm~MokHF6I`wgqL z;)kdf)hz>;KNZ-GRcHUp@1p=hXYUe)+*4KfW!e-Jz>9(|rAFv5U!$wo{aj+6OW=PARqA&W3(wvPJEL6hpEpohYY3 z=QofUSyoiP=yDKprcl62o>HPg0f{=7&yEH^563uFeTHD|B8lbN_@0sa?8CtEpC;sE>5iSI3e)*5#Cv9jL83LTte+ZR%mOd&z}gw5 zU@W;dDn~}z7k}!}eX#iWko$mkChyL->t(nflJty?4L7a$m{|2$KREnzh_k#3S1z)B^aR2P04+~}c z-ciA4Q=;y(k-v5baQsh&!HOj0dkKf)7j=meJzDdRmM8L57?&KgRvowjfV9{yV8j#z*Hpej z@Ky<*t^qfAXA=6tS%|H){Qgzk{bw&dBAG~yYmS6xj{dU}%4zdd_;FTu^KUmIF~2ra zpxA9Ag5>JX=pYxj=GJZ2q(jeF+X0a=ZZ-WbpZYM`(`CW>tDIcO3Zjk7%v;wPicr;t zo1piX+B9587LVD;4K4OoxFrk*lJV2p5U zn@jl`#j1|KsHUcDXivJCVTX-A zAMFS}7Qj;0@;JI=ari#@W6g%tciDRZ$wU7w4U&TH<$IBa=E|{hbf75~3Iv zETVaZV|<@!lG*y5oVzf(x09*8_1RT+She$qRsg*os~iN; zZlFj(4n;6#srIeLOsHc2#|>0=CkJxK(=TX+kSRg!Q$DJe+QYW|wRjhh6UcC=_3M3H zshc+IDmRU96Q1ja`2wrs)nB!Snl)!(S9uPJzn4IY%A>ByoJ}Q(i{6{sQa=Jo2oNGv z!YU68!b2l3(TOW+9DDDEcGNJ~pyTf*?YpMqYz1+ZxRKQv2>NIB1%vf7N*$I`J0Z)7 z>r{nz1*i%@ZyhF}`vnzzKZ9E6Dv;!3@U`fsE|uI?x)Ek+@2ByL6YYro5KXh+d;q%e z4gbhBan*{xm zR8m2vEA@fHsJSY7fX4~RS=)QI6IdFP`1n-iUoPrMuOn$*Be2`H6?kU7Vlv;cNW%0e-|#G9F06oyDve@luO+50fJdjjBUGb2lLqL!v={ zU)r@qyzv@I9(L$^3B)DBjHR1qza;V;BCnRZ>@D8~EfM+Ew8zy?Fb~d7S}5Ew6xN)N zZx|nzt?gL+y>hj66_N%L<+1$&E=-u7=k@MhE2a1p#7tuZq|h#`tV_VQJ1oqLZp_pp zj`g6D!jpuuh|NEYP%#5|0P?*Apjt(+HZ<(g{m*Jdo;>Vzi)t04l|vk&UQ$UcwGsVQ zfrJ+EY~z8=6N5wA#H;lv9r`~jH$F|~F~kH%`>^Ki!izub<1*M_jTjc7U~k7N#tfS3)4AyRQ5- zL@MRaLID)iu!l29RWC8k9JL&7j}>Br!rhDG;YDk&qCI#`f|k0`qfDP7BUXxF;2R_3 ziP|8bRGkrls$pq6aM{Z-%`J5n*lN63QX$G!O+8m8G5kyqi5qew+6PM{@wXaI76j$w@hNaTY=(OcJV zA%xOO#3$Z6cjZj?MM(h};Wi{}=2-sT#liM4i_C96PZfeD0Kcc}0VgsZB;M=VRyt+z z&JQ%0@~>gaM!Ihoh(qG+Nj^D!s4}zOpB-ovJ~o~Z-7nIuWOpDUgG&4bpwh*N>NX8O z&x4@eLMrA2ZsFa+{q#1DkIshrx()fBT(BV-_#R!R#1jN`Yebp+I9P8)$q~{WK#t^Y zg464E$b_e*R=gGC<3qUn=Cs}8MqJ5`OjieryJMG+ITnWwj2Hz*?qrgtzbsT$b(SwM zPmr9e{2)R3`-qS}k)QrEqzLjsc@#nf8zJ-t;vj^srkKa}+U4F~HB4GdNKS}DVEx0%J(Xwcn zs06@t^MI7NWQ?Ls6rSev)bmbn8iC z4SNJ_u<#5W*^tS2Fc??#aNFRNol6AaNtvx^S>m0-fj;o`~$w(jCy6*S$rK~<^>_6ggw$EW#4 zm*%{w^b&&K$wI9Qx#Y(hl||#KgETqePvc#2SC<5>30-5YIaVcqbZ&9m%f-V!8c>qT z>TqXrrWzQSt@f4tNLAqg?V_}KTrgMP$F#>?T<-z6TgkYd$%#7~q4PSx1I|wEF{tqC zEOI?^`Gtr7u2Tv&HHG4iXGq8#6Iw9kgAv`E*j`v!t#B5%xp;n3LI?wcQ6){ueAmPe zX7gyN#RGuM>sq!N5Wy;AKw5e_ctP0tn0c4<>zA=XrwDG6fCn7*v!RBX4Tjp+#n?gI z`QFuF0(@>j4yk!enPuDdRT!N?!#(}Ej?9t~U~yDrRUOks_g9vZ{|y0GneIFQQO!HL zOwm8R(LY?*fcOv^FCeG*mS>EcYka$)SZgrPb5@m!%cw;R1-;`<1$}kUrY*VW1aO4kwQypWXVysYTyoI|8Bm}P={VV^C~Ci;s~tFr49z^*+H`SgZy z2|9@MUz!*l15x8wqe4l|CLZ(>R8=#bvt==H!nW%ZOR)7}v7GW-)O85Q-T2xR&)5W+ zgF!Lol@cpc@OArq-SIE3n$gpC)OdZc@#pw*g|0zidiHHjR(g;@C|?WP#384x+zQ5? z>=LXY?uIVf9trvfg+Grv>YvE{T>5<`ufr=>Y%-0lXbPL$%q;R$={=g;HYs1nuk;~= zt+>rWaqbG3tJ+7?(?|%7(Un>mEux&CRip|YU-=cETp)n1mE+3}22rDdEKm~wlMYuF ze@KF*fZdtzP+URWF7TY7mVWZfMQ}ioP2f61YcAe4UVMeSuj5h^$On>KA5NAwCl-V8 z;`@H&K~}@Kxs)rjS-~(YVOrmN1^pUXI{0lH#|={I?%-sDKOUL)OVl9~%0y?t%-SWH z%80ao`R%#|&;j0*&x-c*s)xvWY=rRdnFevwf>mV?J2BzDrdi*;xSIWdDUo3IS%|N@ z`dHeI)WLazc5p%AwNO16)o3g1V&m%@qTID?2~XxYNSev?n}*vTw7-q?Y(*^F?*xisJ9zUJwsOY^lj$WYa&KTqMKAMO%7%R5u%;Zv|iJ4JJ$w{gHwD4iKLc-6xq%%{IA|tmGhdgd(4DMqWDYW`%kV=Fy>XsFIT@Qto#bSTflRDiYX9 zUQwdi^6>XA`SA0YCb`CI${sb?opb8De8O8)2<`OwR7Pe5j;hwYi{8()eC-Ppzo@BX znafU{1mK0zQ`Ndkg4cJ}76`|>Zu)dIX_lNZTOfYR=P7d0A2!bHfU+lc>;0q;?lKou z_=d1Z3BZcq{!-epwTr=^)GxoMRIAVG)VL1!47?LuBK^veOgAV6&iQgTbE#{oGc3Ov zgN(m$6RCK8N?U=d7~lgpMC~pD-BH}G{P99>a8%RNQD2q~&&{*uW{=O1m1;U?*VY4v zkXm*EJH{^Mpf6ENBWLG=A*dp#abyf2Koj zsFWk)H}JsIs$E0XZBSs(CJLy72pd(^Kdw)@Ue}I|U6SLE!!jL2>HUUBPCsaiiAz4E zJKi4NY3p%2`;DRGVP5v`=95Zv+s{kVSipyvXq|qJ1_a$Go4wD;QXz*vkb)_`U%_gug*J{PmZH#G7P`m}ibhx_Kmqv3iRFkeqKvyiNO z4nhdD1+{2_9X3daknc56xsL#?g_uTt*$^ji%EqavmMa_z?dndt{H=5$T)b8ZurDy) zn#^ctay=IgUnmMsE2~GnB7@ovoIcHHo+xK+7lu~(!NBCwBA?A~v0?gBNsc&pzHlsa zUZiC6w2=!cLyWdeV@3qv?hIAXUY&>F_(XX&1#o{qA`f(NzL6!ZiQkjZD@n`DS;}T{ z^elOGA>SYihr$VK2mg}ot@x772&atMAbuyg|ho{{3~Xw;&`H@YZ}7hm*U`+ zf^ju--f6D>tAxa9Z&9?PG7l1xh)j+zda3DWt5|n|vU+NF|3oGauR0<$IRk^d~$Rg7}-gJL2erkFfVF6t%+y- zpgTt@nvVTNRn-DZ{bF1u;5k5Yn8Gbrn!|)6tifC;|`j9=00*b6M?}B!;>|Sr8XT) zGZiK4Ql@z>!$zg4{)m>9VKn=T!*XI*fX1EL36Z7K9TAVoHRS-|_P1MN)FfO7=yqyz zv5;q$P@R=|A-xF3^u&dacss$JZY4u5M^?Z?m-9kfUYCR<-%oEIao zg!8coxDTLu5iBS44P!jC3@f;<4kP^UJ<7Z+__W!=Q=N{*oOmHW=SCfMg;DNv?3Jnl zUSe0fN zV&1=fv7SHbzs-My+ zKq#L=1ZeMIvYkW!EIaq1dypws#!<%df4ifM4Z_S@Nw-t6N(BGdWM~8DI~FJ~n*>fi zwvsaMt>m;vHPv2MO86--sV9pb=)Q?o$PxlOMnb7=4)ST*zl1=1NIPhhwSl4dc$;e? zGJh4I`MTnk=N8)L_WN5{&LNFN@pOk2WWdW2H!Wc*oxkWJ-Dwi853BFu3S5V=^gz#c zA~{3z0t?x$iVPB6xD2S)d;RTc`qju80X$s7x^$R^hdSK0u^`3^@iWfx3Z7*34JH`- zv7vlsh@y;kz>KJ(Osi!V^=YR-l3Zz9mY2R9uea`7#GsTb-&iV*)ZRL{-*8`_eC48R z1Oz0qDm19q=gTc*#cugs~{M*iULb-<0$TU=g4T#`p>SxNYD~Q7-2zQ)?B2zK_Y`ftXjpjZK1mD9M zOGrSw@(qq;KlQam(*pos-!Q%6f5lJWKOpHS-lrt~Zm^nB1r&(K0l{?usuX|Cl=WQS z0KK-pfk3y4Z|k4^LD|j|_(|d*&|)6G!04zt2&CG1ZU1^8`_Q?`2jl7aa{Ka{_CAB! z0rZln083hobJ4{qnt!m$t|?P%!fAkfXjT?y(PPy_G&QVAKK^yukv}iKOfm!RZYpfl9IAy_zY)~J2>i3)yiYE{*FBJ{-m(~fu}8)VgeY0tO?;IFj5cf zKYWjP0pP|{S*fTN3urV75Kt{dJwFA<_h`-gDTQ+N`U__p;7NyPuBtScH_MtKVt&M| z?NZPm8Dwg%N{#MOo5ncSh60Q+p?5|>y=7jA5YXI>Yn{|ONE@KJT*g*^*crbH4)av8 z$eqtbzc}E7+WHtFp2wGIH4+h98_h~NA{VC@!4O$ef)SPd@zFDgK|d`Ue)~R_*K2Du zp6Tiux5PpUOLsx}TX3^AZp!zs7U&LI1bx)oguYNLpfRoQHSHblNs$#?$A10ycw?;U z5&;U2O*d_0nvy(h@wXqM6flz-=B?Li_!9NvGW;{-C+aBTq>?$06gVu<@ZR{3#heF} z#Uoj$ZPBPt7%uNQ9iGrZEu?$pRX;S&J@?RnY_A_o-F$o(f0c4GPBm|hyxnHr9J$kr zl!^Y-9ZGqV)Eq(l`YC}bWtfA@-4VxVcj~3i+pD1de*U5P=b2I&?z)lj0`W4tQYc!I z7|!w;YB`yI3w3SzijZ=}cPZr2M~}h{$dmd54Z?cY>6bax35HDvoyiCk)oH(6tQA*v z15+11LaIXMXGpt^5+V})5vt3X-J?8%$v7Y1SJ5P ztGhnj!)wR)_PuKGF&?Y?p3y{be}+4di2DU8{3R17pQ?u@kQ+h*beVX~$pYioj9t6Y zg9%3O9Q8)ft`hF{KiD)a?5&|V>`67#$2&f!^lYCPrdnws{^le$KthQ@qE>n z>$fjChba#z5S0Uh2LM#@|MJP*g#lurBdtQ{7=oA03f=6jSxKzfPfoLvhz2!?2BYD{ z#M`ttXYX;{>_vkbtXUJYs`=*SjN?FHj$4`t_ym9egaokM|g_KKCJvuw=_ z$u;^~2VrifHNg>Port6SkUzus2oS7Y1}r#?3c?4{;sJL20xe^cg1bw~As}i446g&6 z^c2Q0QF+qG&+y?|^CCzrZ}5)nNo0vUEoegr5;Hjt3VP@_v&o#;0j= zZ8kY0kKGrflIJk_TugwV$okn$pma}}-n+F~tyJUQxumY^U~_DXfe22)$2@CxBuSZG zlm0#MP7-*}rhUKtPkyOZ*^9bqBJ3wB*l65eZ$v~ffRIX%XgsJAoBw=Nq#Gcra{b(& zynJ62=MA4(rVn3NX1M@+GBZ2DX8uSb9OehnJMN}ROt+{xL!8dPUb&!s+c#^z5+Huq zBQ&7ks}$Dj^}g0j6aff61yKD9llN7KZ~iynNfrjfmd>Ap$4baeoNFZ(GsFLg#?D)% zQ};~TQl}bJSl<3mfDGgpndUK|+pf>sZ0c44Eo$EbU*is_^rtR^ip6yFqLK07tl?s8 zb1VFUfPyKewnMOOSeP(UV$y&wf&T%~4b(sI7?SOHWX5tD6fxTx*Rl#vSW$i@R$VD~ z>CDnOMWM6^qg&M3nr&CXX44~xCi}d(OO`V2FWmlG3juqlDPde7iUPt&mTosRG>OIxfvX}(53eyH0DeuB_42Sp6@+dKf< z)*##zkPFI$30H9mxTHsRB&HIbfav?q+w~<0h6e>b_doZN4$}VxQ(CE9OZ>yN`l{a- zVTtkpAshj5|2xt9vzB35{XYua9~%105C6Z{OaEYcxPL*H`d=Bk_a^TDM-}!jUi&xb zy8bIe_s?0~;eTc5{@1NeIu!_s3P7X#tEJ$6aq-J^E#YT-iS4&4E|_Dht{)rbHL0#o zXH+A=P$PVg=O+nPFY4`u7o$jVJ3YCKtcSNzN3lO$WQ=tkBFT{P6TyvTu-BPpS-kRU zMX^8VPfpGzIS8ZJa#0thGn6#o(-jkqOc6)44XKYQfB*I~g0|h+-c?+!=v;{d6ybFF z*`3hUx$P9Pr022eJ3Ib!*9O_nqH*lB#gxGZZbnV|M7aA=K7 z(!f%Ug4v>o_w|Oce=L|&sJovR7iY!6CDi>y9XTx6bBfsYWMV5AeQEVkRc8YB2iR*K zgrG{y9c0%S9559Ff|VNDSOKvs6%WYD0d8#ZzkXVN{?C5y-&?XizAZ|kJ*zSSN8p`O zQKo0;lVh_|&meNvaX?MP)N}`R#v1<>A7+CgMiykuDP#~MmFGv_lHbiHtV{EJw+Wsh z-Ph!rY>$y>Z2cSQNm^RYP(ceNInICk)H~58M)1pFQlZg`4H{=RNGc|)lyLgP{W2*! zz3Vxx_ymmu%_R|ywiM9t`zf7t;`nT{vJ^N3-h#PqwXn3Gn~f$T>NZ)_wNma zC)jLLJuaGr-b##?3^~F^_(}lv$6=gbQQXGXD~X7FOV>L~*J0Q#fMcAzM*p)fR74zw z%x$d0mYs$;{i;k8op6XeUJbPiULv|QyVX~2g%eT;?q3TCAar)mxffpIBD&#?qPR-} z*|qmgV(H=btFb>LWGk0C!4W?mtk^x8S=mQvzHOXZsHyJon7*w@XMAs2s+Y)l)2kOb zc3r=^`@wOwZdu}#W%#95>>Dpb2no9kdqxe#<@yDa0Sg&~q){Bw%3!pFAd3GCfwdZs zOW4hEb0GxMAQh5rwQw5O@kDbfL%^>fGBLE8zGVlc_m5(@u%=^QzGv7Xcf|4f5My(= z&ZY1Pylgnbq z2`+)#0EXSZ%9gg?i*r#FUHiy%p$L+54|T zA-V1C(4uYG)$&4_Ty@jUdf{NFPq*7a8Zu#1y7C{cQC3J7o&#TGx4?dc<^k(-65W?t z$Pn!c_BF)#I?0C)jB@qlR}o9WxMa{?W@3K)IVgP?ss0LF^ubX#1o!6lNbqM7ouk*K z=~Ba=up=+QiDbq5ZqezSCypjP{yOJ`Wr?tvOq0pA17pEY3iQWE%|I}rQ9OQ;IFED* zDg)Bb?;c-LHk@Xa(~H^&$yHwkn!hO)Y*p}v4|YjSkENu_q5gUF3#Qs&S4Vjlea z#1A$ITV`|<6bCZ=j$*y3Zm{si4@}Bi20DK%j=-c9JVlfJ$N3MLI15oHnT2cbb1wh; z{D;npv*M99;RcwLhT~{^YHjswWl5S{=?Yy8De!#k*O8ZvM=nbaj4Z{_p<)Lx-!9bV zqxrfA=(^IKhFR>HQ@%UM%X|l4S>gXmDvL zuP)Y>&04ddM;ACPFZO=W{VkR%#ba5a&aZIFQ8zJ+eGvSZQR<5r zh|T(A7qvqlhV@hB!~DX4M(JbPb4AlOETLk#R7`SvdV4m0`fp}R#X7d zh`UZt?H=A$)E^fKspilQft{-Y9BXNybN9d{!;n=dbprkmMI>lN#ebht7Hxi$-uq6h zb7lw7p`-IC3vyM%yKKGwNSR3bYTyp2OQSf-F0 zkq-e6L6~Ehsdr2_Ik~&-dLcd1zv6*>m>KoW#n5NG<(OwwH}Aj`fMPGCJa97L>LB)7 zG1=bovKbMRgw_8;9@{>Pnja`b4_eRSpbVdI=_)Prb~iB>BCV>Enz0l{zE@n;Da{dA zBuyZJ*zbnlIE0)1HYVv4n9ykL_7FlMT)ls*si-&}4#~T0YH^lJ=Q@zgA~$8(`uel_ zUEiiaNMk?^>0kOj`JvZEOF8npk6h)Bs0hVCFiagPKRH-1Os#e?m^I3AQm5EGZQ1g3 zRy@-r!Zo+r6CNa^iT)6oPXn@Xzv1PNs;JVk=bv{aRcpztKju0-vT@9>n>p&H5^Mf2 zlDv~pQ&Je`tdx4%HF;dEzf?W>?f)WOOOhQyNEYFZyEr9`QjMzz7F8`{yAk8v)5M=$ z@TK1=VR&=qW#dfsZ4c4^M(Dbs`Eo{TQHjmHU`OABP-yufKqOpWytk9pNm)gnqQy+i zN}tZCbLIU%!bDYpkYNC)zfi^v$Ags7T6=J4)kd!s?)I%m3EiS%yW`tqpjn zfuXxgKvF`wOF&S%QM!?kkQ8Bt?v|1g5J8ddkZzGKC6$yeLGl}Y8z{ zm(#B;x7UBH?s*HXz{RIiwLEb+OwIY&wr zf?NW){yS3u8HdUNVyt;s&@dDpG)l$Zl1|;lODV>ql(Dq>sQ8`*B&8%0cABarAKnW8c+8mh0OLXP*yf)BFCMOqy$7b!} zzC<8svy4(;5kJa%<g3TaChEVBmC1N z3&8V)BH5-6pd-*nX&W1ifa``xyMZhrc=kgT{!76Dp! z08h#Ukw<~s7wxCAz;`!g2~=u=7Rznf)b)1+)izR;#NVifQB9V_5VF|%lr8Fr2#+NO zmuIMyH*=WyeKH7M(l>Wa*<3lkccqj+`5Is?Z9z~$-lLr_-DSc?e6opZ*p}?=R7XJp z%EB7S*@*{w6)l73slHcQRUYuEw?GH45|{g<;C^5*8^VrR4{TfoZa3=L7akF5;t6v?YDX-! zlU*qvFaajx@kK2>&=9s+2EK%DRl5~3AD;C44x8P=x&lDP6^fDCx>WCz3)@D+3K@Ps{5pukxC1?*DA(6&ukcSw&iV`QxNIqBZzCd z<489w`*_Uxa;L-)6f(H1xZV?P^MRhVlOwmjc`!`MY0!Hi*!tw+4~O z>3qS^eU;uCnm%n>!rh#`R?@}JWZ&2k z)XVviuvC9Y0#_-|nE4YJ4(Xc?QHX)1v`+RMd$Yyt|AsDjvJEySPe`yL$hAv!noicv$YR2TrX``23}$6=P$nS*(F{ zfht?&IuBW}LX!LKFh!oE^N*w{)*8FVl%BkAY#(e*(w&%uJ_yAyVM`HXz&>EX@Kqn9n?vQY-!@mMjIty7TV zp2eJ99f8FBf#`hh^@3JRdqVj>;-nB{N1eC|&@|_stojqQDS^FVLcbJsy^KBW&pCN+ z=%MUMhQa^e{Fn=VMwN-=z-5g1?p*Wlo4v8tI(e*iPnrbVC%v5Kw|w-8@+NNt<~bvN z=^X~1bZ&$AYNcU9hjQ#*>Z}LI3V2z<`FTr+Jg7(Rt7-Cx;wSNv>O|u6OA}h1HiFF3 z_RCUTVJ}tQqEr07|5JMeV1Jn2I|798W!OPgh)ey#zeMx#nOw@1G?hCxT6{rlkh;P* z*Jr);xT2yzoyO=A-eTH2z)fN+-O79A`E@%H#a5|EbIUhDmoXUp0g29Nwgtsp89W4{ zQOj(hX|bZQA|n~Yb#;XpNvlXW16-h$@nK;@Q4*tpk-_N_lQd%2wA$spyUn6%R|M?5a59M_*oWaboj`7*0Dz?nh20X{di5T0m4) zOuDGI)jmksHYIybKPUbYM_P?$M(&9FJb^lvoYEN5C*AMd6+Muuo_YU;;00}UNcr9& zZtwrS|e(Wh?z{QXm+wkxQo~`v;5NiZo5FoV!9*Q766H! zSp$Qf(^4Doba#9E(o=uQTe;?krpIbMeC=@7$5i?Sb_i3{lnRm`woe@LQ@bu=0hQ&1 zi!n&+cK1GJO4MY`Bc(s<(WVPuse!(I!U=`?+h!C(rc?UEq*j#tug?T}wVnt(>b$0f zm)vO{$(>n3O|!MoX?1o}VQeT%N9#*b;_&vmJ@1{TTiUYIlpusS7eDL7KF?!LkX*Hy z!d<{{@Fv-Dax@ufLKJQ4osgvcRO88HixN}f!oq{d{_)hT^hCn0vT?7fGz*r6$6EQe zp5rC4Qx!f)VjYDwx9z3lbvGKhy!P)-fJ$`riLH#>Z!822t8M~F0&?yKe3txusaF~e zVxbJzZPuXqJ@5+;3$1(Pv(7TQbghStEX-I|wvUg_%L0+Y3Hv02-nr{p&js>xRCJD0 z44fxFk4ZLo*Usmu)yeyM8kYAX6_2L6s{OeqztnVeSW&eR-?_YqI%yK+2?Arx zDQHj{J#U@B!pS$QltS<6I3(XeaJk}baE^F8y8s1+U*9`g##Bc|`N$Akwtm7!zWmTo zDZUd#t<)Pv<>-@b{7fi+49ovm19w=HXyp2+*Y-JkZ$|mkyo_q}HlYAQLKzgK${`G< z#{?sSrunn4O3I!cM_1<9zbYQ2rpXyzI=CLoJp^`2tb3S?@d=D_PoMS%vu&w~x?`X8w7PfFLF@?PS|?DW08^p-q7_Q<_QF z7|$CAwbD+^l=LYd&21gVpC#hI_`JbIkWxZb2^C$-OJ075o-V&2J(8laN|p$ zzs5KtJ}Ixl3<2)tsWu(JT_ju9p4F>8eFbIuxsg-n($2)Cl$3rwrz@mWYv8q;xEt4m zXwkIOs2J`1N9SXddlnbfe6sc{4;0B6wnF`-^1$6})s(I{@`8eb(?!!AyOI_M{{O2m zXp?x|Lq`W?nFAbD+WI2ie1EA1U_REcQLSOjCc>IQT97-nUd-j0D=+iqPklV38}Aju zYVs@>;i%|ZyuXIL`ng9*Oun>k)wF>RmP!0KUZo%$AnBdF^QIQ4t(tF~OuyGoz4* z^aN||l|IZa<-12>iTAAcV$qsOfyuOaDy+$k5eioy@SarOE5*MOitZQ>F;4kLlUu!LQ>|^Zj(%iPLvyS6Ngqb9wlz;cOMoyKR3 zuyW6@_9XzMC2$A z0zEyw@KH09VcjtTmsQp+Srz&CvRTJIM}$yS<;EQl8MI6kH8Q-jZuR$%>>cgvn|5@n zl~ldP3`t5#3c&;S<*oT=7x2(Sh)MEs2sDS%9N`v5U8dCW_g+Og_bUdo=C zSCN4hdStXyq{IFU-z}d#*Ck`$onX65Z)Lh}eIfc`AvSH$GE{kS2DNIAeRsPF9UVi5 z%_seRuK_aYFAg4vEcx zRSQ(Ezd4G3Gk*MEEf{pss(7h2RKKy%{hPD+NiF_SMSu`g@ZURb{*)FDxxj%(k+jfQ z0R;hF$@gCnH~;De1PWsC?+pTWMjZdWLHtyd_(zul2tlL$dxL7M5*GH`zFMMHM3N+K;>L2+SU!BV|@%ed2z$#CE2>;Thlb@e0dol z{7keMJ4z8H+#}Vwgdkax9p~?dA>|T@NS;OV{-~2HC{8CFyP---15Eze_@;hv%XfFo zTRncd>luVe9Db&PB_$I_s>)}j25eknZz#zVODrF&n8uY!6`CN#iC821r+rbMN^-`V zvyv=?2)hbuZL!u3)T>D>jlb=kd(ehW-e|YIp{jg)&~Kx6a-F!K)z41La8OOrq!>Ln zR?KKLL1MLm;(2c%#<)l8RPYy+)tS}3*5dxio!Fio)A3Yh;#aTw{CM%qE>LdW zl}G`*bf_vlv?S!t2z!9Gb{~}Jyn01+mYwj9E4@v?ZgMIT+hknuiC3^gxccD6=a6SL z%K6Pk$s3Ydq<5IfHh^OLPJ)SgFk32$1pVCzK%OffSb4cW&j5@d z^(3JUTLnOSUtM9{mg>nAXnR5jL30FL|1E8UCL{*m5|RUue8Cz42xFLGqdLnQqzZuM zCi;DzZJNZx$=9<&CmQCO#E>Y2g(b|a*0P*JHMY~E(<8WX7Of1Aks~4^phl#7t3l(u51V%*n%P>dg` zHx*10!jw`6B}Sa@d>T6?yyC{M=GvM^P4*-<2hb&rU_Ft!T~FG_5-->Mb@@D6@?QeU zlY!DfJ8vrsn%SC@D=xAOK}$12u_dx=w>Rl`rL#!x1<5VU4M=^xAI|AXTE3zMToPGJ`%{;RrQ!MN=8>V;`uHXyqV+kIcs z-xiEP`@`uR4%yi&%(9^?OGWKnUT<5hXsdJihODOWGzyERZwWsZym&;i^s&YD>|BhT z`UdGKGGo^UQO?o#wZ317t4Tzbd3BTJswk8rmKNN|elq_M~gMJYVu}QfpN{Gsp(jRA$s9*fRFoRoam(bBakgiHIC9AR1uF+>uQ^7TwJa5cLpmR=3aE_D) z1cVE?PWi`>mBGZZJJqJCX~boxB5Lwz<#nZ>W&UaLSq$4*D7 zfr0n6BO|2n+Jls+l&C=NKokm+rd)G#tGjL;%MX0Y`K0V89>^F#I}&N!(*P5K!>9G8 zb#?pV2N9dfw}viNcq79XtdeT7LWwYzG;+2TO#9@{Q*3j~R~R)PO3Iaj>2;g&3XRdm zxsYv15&0qz$U6a%W`G|%q4DEE;EypK+ZeGgMHHHuIwQgx_qk17K!wf3t=tnlM@Nzq zA5%RCz0?zCgL563uRz;fAqYqiu>0r#V*Wa+hc9dv-HJJih|dOWh*;H~vs|{1A}we+xUWKJEzxQ(pD6pOWuSV*l`1@mr()BR!ix z#~5vk5(9dLn`uru9N#;{80Sa{KtLgI0`8}B%0TDH=e8-%&%)15Ym7nU&EJ$OffI7D zRSsHLC*cI#&np+cm-J^{<%ggH-~=43jYDUK6L2s$P8CkT!Q40xH~|N9ur>~p3r@hn+&Eo00S9yAz;FT%=Emj22{@P=HwY)-U~b$ooPdM1aago)0uI*3 zVadY@IG7vf2q)lRZd@{)fP=Yl&2Rz^=EkkU2{>3Ahm8v-;9zYWwji8^LZrmiCfP=MhH~=^S2W#VSnBfE*%#Blp6L2s$&I3-s!Q8kkH~|N9 zjvxrTehB$H5TySBG!n1j diff --git a/dogfood/20260616-default-semantic-renderer/videos/explicit-libghostty-vt-webm.webm b/dogfood/20260616-default-semantic-renderer/videos/explicit-libghostty-vt-webm.webm index a44f55e321c4a87d21f04e9a7cbd078f03b0a4df..eb7ef258bbb02a3e7b2dceee4f3676a848269060 100644 GIT binary patch literal 27083 zcmeI52|QGL|Hsc5j3ryyvJJ8o*|&(9gpg$4%90Wxdx$V&iO3d0$r8yHiV$LKW#2>D zh7?)KzK)syRQGxAeg4m#`~2^{&-4E`_x1mtIh=Fm_d4hE{(jDHIcLtihD~iED_!L# z5~Q+;pI4BiDyv8em7oxBTbrvYVMr2{P$UVf0m2Rhe2pvj8P44ItU6O2X;2Wtq?vcw zpb(k)eT~_$$PvFbc&7R`{=NN%jdliwL7>CmS2+x-@oxrQ30?TUAHeL}?*0>hzN#uY zGW0Z*JW7=QT!w>ZgS!uR#k6>!=IXd)9_V%SJ6+})P|5CkLK_NnZw-| zqdgf@kpS=W)ZlB*m%@?2%}`@2V_o@RByAI`2N3Qg18kF%l30;=lkkgEhbku)3`6W7#1m zr{1C70WXt({u*xp?EC;CYYYkK0(z9cCC{AG~;g8+9F=eD455?BJ3@mZzs_`6#G1)3{8 z@P8iGRsg&8pP4A(OLQvNJlhw)2?DWx7nJ;SW&sN9?-3Ljfc`Ub*sUqyTgfKNEtunY ztJ9GnIKZ$UpCMO(b>HWf_RCs~9 zzswj8_;We(F^_Z03VIMhZ0~Z@VR4Re3sKYLuv3^arF1%zi18s zil2*{0Th)2Bip^a?0Vrq))10}0_goJ|8K=fY5)q{A5_0rd1^yq>+hU7uIdB#ly%i2ac_Fdj zHXaOQMR124_2omYFBEk>alK;Q%6g&|7LT*VKHtQ391};FQCAQ6f(kvPA!U&8ndNHH z$I|8qY;~J*ZyD*!0rkL?m*WR zy`LXe73#@+`~{4oQTC>9u?h&?zt7_w^vBYSg5|SHgLT&>2mtaOFO-tGSWIRb)|@?0QyC} zQ01)&sWKPS>$uETnM@69L4{HAFyAcGPf#aE-UeZ|s}h~VX6f7fU>rl^Z7p!2L@9r1 z+@<1V1h-rFiVR2U$7Az-zJUkM#_!$6Z3(*EJ>&>&zu6hMv3OzEXh17Hm|H;f0E6Z6 zcgiW4BjsF3#`OiArL+e%0VV4ZY3$avx%sS=)0eu&+~VR#_!<@&taZ5yc5-pC!R`6+ zW%At@$^AS+nq$)MQMMYMWXgEEg{e{Z@C@IbXsl8j-Pt=7XT)pHVk3W@v1f^=lvQLR zN9n`NwJ$=P7Z0C`idu-+lS=aR3rzQK#J$7a6I42Pg;QeGrncGlYA`T~Y9h;SMncOwQ-0qu+^RsMFNwKu1%;69wbOx5yBo_5o_S3jaYQ}2d1n@; z?<%<*=h}BCV1LP23@a&Y`i>#o$i(PwCZu`WMs(}Sp#S0PhKu{!@&t`=mDk1lZpW5@ z7FsWBD6X$~;Bd2cww+eOurF&&y&REyeA|yu_$C&GBtZai4Bt*duB6_~iUlVGW3JP+<{F!MKT4b2WBNhw z!1Gf$6G-vA+e}gT{T(}QJVG9lBo}!4y&ioX?98m;2Ls>51JnQ8+As7U%QR9Z4Tdk) zX9-%vb}E$u&&4Qtdff>~X~ApFK$7(QO)J-857qiLg-mI7)YnYA17OVV?+IY!Pfe$R zW|Eg)UAWv17+ztL7Q}Hg-x@D56-lxMxcxGnBsFq~z>xog6QP-80JnC%VlF%?$;#aI z#tk@LUK$e24aEJEy#JFE6`#OsxFO2G`t$xw5~~2F=4*|@3)J{=1lD{a$31$|ZK� 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 b=jgO?VL&jE`}f}!5K`nC2rCj4;%RPTFA@#} z7YPM|RCMbiw;2hPjdeq&L5?!p_#7@MiCK`4~&sB_ORe!W_W*?m^6j| zAt)9ZAyljNXBqw|{CL&h$|gP!1ativQKTjWK!q^^W&!~uKQe>e9n2ztfUr;vLk%Ua zU?3Rjc5n!|i4I^Rr6vgcp*08`Cmjf`UK63-^!}$A2>u5GPp+;xrrm*-Luuj#;o=IS zielm_V&VT#2v^rR)9z@~f6dNvDEIFZC%sp|^t}SCe^h|T&f3Jo$;)ymPrM*POkG7- zO4GoP* z3yFXL2S9&-3Z`skLm+!LSXt!m`xiTevGNfr(XZOUE_3tb|JwhH!2eGXXa>B!4c< zJWyA|8B#Txl^+TgfdDGN6f6J%%?N-4z;V1m#J)iXVE;yVg1FQEaC@dDVX4VI?wYy( z?>mObXa8zWgx;z~=1EfMX03b(0R@9VfD#}9=3hwf-+;G6g{OizJI;O9?*Coq>^E%0 z-Hy|H zz^Vc_5fBP(<9_Mo$089t`KN?7#ghS^%i?{~-I0f4-?6zG+?#z3{!aRw@uo z2!Mw6UVSG34&vF+Wz(lGU@rjh`-uN0;QvFi5Ohh3KVCSU(*p~3fM8YtXZZJmDg6U5 zMhMOS+yiZ7eXiXV<6?K|1t^#Uf_(whzV}^Ju7AkoKk9S;ug+`R2j0N8;alXmuQX*? zMArx?XaIsW1JEGf$x{72*@G#xzc(n81{7zb>uU%2p1J(L|NLs>udn{D*S_=Ozx_%Z z0NYHl{5VDONjU;gFa`vN{Zne7{;#jV+us@JKUIU*X30C|J5t1BK!IH^5S$5cnFauR zr$;cl0~36Q_9A(4eh+%vZwYsQ4SrdFW<09CK>>lz-h5wB?i~ltR9d26ICo&W8MnIn zyK?=R1B^hRt&tZ?UIxQA)t8bQ!X3d;!7k8ekmMuiYU0pvuuDlW_zm;|d4qDP*sxwh zxEb&>AP%Gs5`N`>0+U%Nm7uLRD5Q;Pc^WKKzsLFxOaoiL)$W-yO1nT+*ss;{h< z;rEhr+0U=Nub#Iv_w_I9JNqq!N&b9Z2Rjp|IT34{Aj)T$>wtlr{jQGn$S&o#?{~4! z!Y6_?UBg|JpzOCkzo55IFQ1;mch+we*Fe3iL|*WLgueun0z%$y-zeX5o)xnvlAhjyd;?($ID$-Hn_e{UCr$(2O#%RHI5%BMw#n2M4SCg^KlP`dee`F5=ZW|xK+xtq{T9nQ_cQ8Zjq^v3jd(9 z#<56j8)oLL(psifW!<(EOZ2lB!WPdn{5F*p*fR~bfRX2vNvm3@rbS9sHJKa2#??V} ze92<+hpraWIpGHEnjY3Dzj3wu8tY4%uR1U;N)UJV&{@+#cQ_PIdf;P9U{Ct|rP^D^ zogfWAX%Ai$Q{TAJA0H9cdi#6)zrN+WbZC0pzM}_oy6^=dNr8M#|Gj8S_4>z5lfC^z z2iW}{ok|I2PH6y?xn1Vj#ami{pin1io>D(RTEAS~34H>;r7hbv^XlZl4#nZDMYQ}^ z1;tQSbDOp9%xny1Ll_zqw*hzPr=aClRp8_A894}+Km6}Iw;P6Fm zV+0f`gRo$fHfPbGpus=PaZJy|zpUmT)Q&|2+EjCaZ%Zwyw)jF5&iy^*XGkq5KfNtF zL(>9Va{?K8&-$|m7yIh zPcUCen=wbjliL;6U?T5&%N{&j;LQkfyz*k^I|DIfT|Pub)NuJw>p>0(;5L;B(MP0q z=Lf-45&ai9aK(Vv*;TRL&BYH

o$N=6p}SdbijG6q*^nLO*7nRBr$(@;d$yg}P{j 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

(D|gJ1V*}XY$NlP$mH4)s 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