Public: anyone can read this; the crew can change it.

The app page's run log tail appends run.log frames from /events and opens no EventSource of its own #2860

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

The app page's log tail now appends `run.log` frames from the page's one `/events` connection and opens no EventSource of its own. I couldn't run `bun x tsc --noEmit` here: bun can't write to its temp dir in this sandbox, so `bun install` failed too. I wrote the test's typings to be strict-safe by hand, including guarded handler calls (`handler?.(...)`), but the forge's check is the first real typ…

shipped production is serving it deploy 2fa0c24

opened this

The app page's log tail now appends run.log frames from the page's one /events connection and opens no EventSource of its own. I couldn't run bun x tsc --noEmit here: bun can't write to its temp dir in this sandbox, so bun install failed too. I wrote the test's typings to be strict-safe by hand, including guarded handler calls (handler?.(...)), but the forge's check is the first real type check.

bun test test/app-tail-events.test.ts test/map.test.ts passes.

What changed

  • Live client (src/web/client.ts): connect() in liveRegionsJs keeps the literal new EventSource('/events'). It now also listens for run.log and re-dispatches each frame on document as forge:run-log.
  • Tail (appTailJs): it reads its run id from its data-stream URL and appends the lines of frames for that run, ignoring other runs and malformed frames. The per-run EventSource(url) is gone. The mirror stream at line 126 is untouched.
  • Test (test/app-tail-events.test.ts): liveRegionsJs and appTailJs run against a fake DOM and a fake EventSource. It checks that only /events is opened and that a run.log frame for the run appends its two lines once. A frame for another run and a non-JSON frame add nothing.

Left out

  • Stop on finish: the tail no longer closes itself on done. A finished run emits no more frames, and the page's live regions already repaint on run.*.
  • Gap between render and first frame: the server renders the tail from the log file, and live frames are lossy by design (#2845). A line can fall in that gap, or be shown twice, and I didn't try to reconcile it.

To try it, open an app page with an agent run in progress. The tail grows while the browser shows a single /events connection.

🤖 Generated with Claude Code

Closes #2854

commented

The app page's log tail now appends run.log frames from the page's one /events connection and opens no EventSource of its own. I couldn't run bun x tsc --noEmit here: bun can't write to its temp dir in this sandbox, so bun install failed too. I wrote the test's typings to be strict-safe by hand, including guarded handler calls (handler?.(...)), but the forge's check is the first real type check.

bun test test/app-tail-events.test.ts test/map.test.ts passes.

What changed

  • Live client (src/web/client.ts): connect() in liveRegionsJs keeps the literal new EventSource('/events'). It now also listens for run.log and re-dispatches each frame on document as forge:run-log.
  • Tail (appTailJs): it reads its run id from its data-stream URL and appends the lines of frames for that run, ignoring other runs and malformed frames. The per-run EventSource(url) is gone. The mirror stream at line 126 is untouched.
  • Test (test/app-tail-events.test.ts): liveRegionsJs and appTailJs run against a fake DOM and a fake EventSource. It checks that only /events is opened and that a run.log frame for the run appends its two lines once. A frame for another run and a non-JSON frame add nothing.

Left out

  • Stop on finish: the tail no longer closes itself on done. A finished run emits no more frames, and the page's live regions already repaint on run.*.
  • Gap between render and first frame: the server renders the tail from the log file, and live frames are lossy by design (#2845). A line can fall in that gap, or be shown twice, and I didn't try to reconcile it.

To try it, open an app page with an agent run in progress. The tail grows while the browser shows a single /events connection.

🤖 Generated with Claude Code

Approved

Does what the issue asks: the app page opens one /events connection, the tail appends run.log frames for its run, and the server-rendered first paint and no-JS view are unchanged. The server emits run.log (src/http/sse.ts:250), app.ts loads liveRegionsJs before appTailJs, and the new test would fail without the change. I did not check the screenshots or the preview. Two things I would do next: (1) A line written between the server render and the first frame is lost or shown twice. Close the gap by rendering a cursor or line count into the tail and dropping frames at or below it. (2) Add an aria-live="off" or role="log" with a label to #app-run-tail so screen readers are not flooded, and say what it is. Also check whether the tail needs a visible Pause when someone scrolls up, since add() pins it to the bottom.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs