Skip to content

feat(host): a window follows an agent's transcript as a stream of pushed pages - #4175

Merged
luke-agent[bot] merged 3 commits into
mainfrom
luke/transcript-push-host-main
Oct 20, 2026
Merged

luke-agent[bot] merged 3 commits into
mainfrom
luke/transcript-push-host-main

Conversation

@charleslpan

@charleslpan charleslpan commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

The host half of making the agent transcript live on main. It's stacked on #3298 (luke/live-transcript-clock-edges) and replaces #3909, which was built on the old stack. The window moves over in the next PR.

What changed

  • Act port: a new event-iterator procedure, codingAgents.transcript, takes a transcript key and yields its pages for as long as the window keeps the stream open.
    • The key is an agent's own transcript, one of its workers' (callId), or a task's planning worker's.
    • Opening a stream asks the host to follow from the start, so a window that arrives late gets the whole transcript.
    • Closing the last stream of a transcript lets the host's follow go.
    • Each page of an agent's own transcript notes its title and status, as the read did.
    • web-host serves the same procedures, so a web tab follows transcripts exactly as a window does, with no extra wiring.
  • Gateway:
    • codingAgents.transcriptFollow/Unfollow: followTranscript reads on the transcript's existing Realtime subscription (transcriptFollows) and tells each page as transcript.changed. It reads the first page from the start, the next one right away after a page with messages, and otherwise again only when the statements move or the service's wakeIn edge passes.
    • tasks.workerTranscriptFollow/Unfollow: a planning worker is followed on the service's held tasks.messages read until feat(service,host): a planning worker's transcript is followed through the shared live layer #3311 gives it live statements of its own. The follow stops once the worker has ended and its page is empty.
  • Wire: codingAgentTranscriptKeySchema and codingAgentTranscriptChangedSchema, recorded in hosted-wire-schemas.test.ts with goldens generated under LUKE_UPDATE_FIXTURES=1.
  • The sealed statements stay in the host.

Tests

  • compose-coding-agents.test.ts: a followed agent transcript is told page by page; a transcript the account can't reach is told not found once; behind a closed gate it's told unanswered; sign-out lets it go.
  • compose-tasks.test.ts: a planning worker is told page by page on the held read, and the follow ends with the worker.
  • agents-procedures.test.ts: two streams of one transcript each hear its pages and nothing of another's; the host lets it go only when the last stream closes.

Checks run locally

pnpm check:affected gets OOM-killed in the sandbox, so I ran its pieces one at a time:

  • repository-checks.sh, biome check ., oxlint, knip
  • discarded-effect (with a larger heap)
  • tsc for host, desktop, web and panel
  • vitest for the whole host suite (451 tests)

What to check by hand

Nothing visible changes yet: no window opens the stream until the next PR.

Made with Luke

View guided diff Turn on auto-fix

@vercel

vercel Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
luke-web Ready Ready Preview Oct 20, 2026 9:39pm UTC

Request Review

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Base automatically changed from luke/live-transcript-clock-edges to main October 20, 2026 21:24
charleslpan and others added 2 commits October 20, 2026 21:26
…hed pages

The act port gains `codingAgents.transcript`, an event-iterator procedure.
It follows one agent's transcript, one of its workers', or a task's planning
worker's while the window keeps the stream open, and lets it go once the
last stream of that transcript closes. The first page comes from the start
and each later one is what moved past the last. It also notes the agent's
title and status from each page, as the read did.

On the gateway, `codingAgents.transcriptFollow`/`Unfollow` follow an agent's
transcript on its existing Neon Realtime subscription and tell each page as
`transcript.changed`. A planning worker is followed on the service's held
read until it gets live statements of its own.

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
…er than asking again at once

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
…t the asks

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
@luke-agent
luke-agent Bot removed this pull request from stack #4221 October 20, 2026 21:45
@luke-agent
luke-agent Bot added this pull request to the merge queue Oct 20, 2026
Merged via the queue into main with commit 5de6458 Oct 20, 2026
12 checks passed
@luke-agent
luke-agent Bot deleted the luke/transcript-push-host-main branch October 20, 2026 21:53
luke-agent Bot added a commit that referenced this pull request Oct 21, 2026
…hat draw them from Neon Realtime

A workspace's members, and who its work names but left, are now followed
over Realtime while a window draws them, on the act port's own stream
(`workspace.people`, an eventIterator), as #4175 streams a transcript: the
procedure counts the streams open of each workspace, has the host follow it
afresh as each opens, so the window hears where they stand, and lets it go
with the last. The host follows each workspace (WORKSPACES_PEOPLE_FOLLOW,
polled while Realtime fails it) and tells every change, or why there is
none, as WORKSPACE_PEOPLE_CHANGED, never through app state, which the voice
window reads too. A web tab serves the same procedure with nothing of its
own.

The members read stays as the first read; the re-read whenever the
workspace's member count changed, and the settings page's read before
Transfer ownership, are gone: a member joining, leaving, or given another
role elsewhere is pushed.

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
luke-agent Bot added a commit that referenced this pull request Oct 21, 2026
…hat draw them from Neon Realtime

A workspace's members, and who its work names but left, are now followed
over Realtime while a window draws them, on the act port's own stream
(`workspace.people`, an eventIterator), as #4175 streams a transcript: the
procedure counts the streams open of each workspace, has the host follow it
afresh as each opens, so the window hears where they stand, and lets it go
with the last. The host follows each workspace (WORKSPACES_PEOPLE_FOLLOW,
polled while Realtime fails it) and tells every change, or why there is
none, as WORKSPACE_PEOPLE_CHANGED, never through app state, which the voice
window reads too. A web tab serves the same procedure with nothing of its
own.

The members read stays as the first read; the re-read whenever the
workspace's member count changed, and the settings page's read before
Transfer ownership, are gone: a member joining, leaving, or given another
role elsewhere is pushed.

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
luke-agent Bot added a commit that referenced this pull request Oct 21, 2026
…hat draw them from Neon Realtime

A workspace's members, and who its work names but left, are now followed
over Realtime while a window draws them, on the act port's own stream
(`workspace.people`, an eventIterator), as #4175 streams a transcript: the
procedure counts the streams open of each workspace, has the host follow it
afresh as each opens, so the window hears where they stand, and lets it go
with the last. The host follows each workspace (WORKSPACES_PEOPLE_FOLLOW,
polled while Realtime fails it) and tells every change, or why there is
none, as WORKSPACE_PEOPLE_CHANGED, never through app state, which the voice
window reads too. A web tab serves the same procedure with nothing of its
own.

The members read stays as the first read; the re-read whenever the
workspace's member count changed, and the settings page's read before
Transfer ownership, are gone: a member joining, leaving, or given another
role elsewhere is pushed.

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
luke-agent Bot added a commit that referenced this pull request Oct 21, 2026
…hat draw them from Neon Realtime

A workspace's members, and who its work names but left, are now followed
over Realtime while a window draws them, on the act port's own stream
(`workspace.people`, an eventIterator), as #4175 streams a transcript: the
procedure counts the streams open of each workspace, has the host follow it
afresh as each opens, so the window hears where they stand, and lets it go
with the last. The host follows each workspace (WORKSPACES_PEOPLE_FOLLOW,
polled while Realtime fails it) and tells every change, or why there is
none, as WORKSPACE_PEOPLE_CHANGED, never through app state, which the voice
window reads too. A web tab serves the same procedure with nothing of its
own.

The members read stays as the first read; the re-read whenever the
workspace's member count changed, and the settings page's read before
Transfer ownership, are gone: a member joining, leaving, or given another
role elsewhere is pushed.

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
luke-agent Bot added a commit that referenced this pull request Oct 21, 2026
…hat draw them from Neon Realtime

A workspace's members, and who its work names but left, are now followed
over Realtime while a window draws them, on the act port's own stream
(`workspace.people`, an eventIterator), as #4175 streams a transcript: the
procedure counts the streams open of each workspace, has the host follow it
afresh as each opens, so the window hears where they stand, and lets it go
with the last. The host follows each workspace (WORKSPACES_PEOPLE_FOLLOW,
polled while Realtime fails it) and tells every change, or why there is
none, as WORKSPACE_PEOPLE_CHANGED, never through app state, which the voice
window reads too. A web tab serves the same procedure with nothing of its
own.

The members read stays as the first read; the re-read whenever the
workspace's member count changed, and the settings page's read before
Transfer ownership, are gone: a member joining, leaving, or given another
role elsewhere is pushed.

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
luke-agent Bot added a commit that referenced this pull request Oct 21, 2026
…hat draw them from Neon Realtime

A workspace's members, and who its work names but left, are now followed
over Realtime while a window draws them, on the act port's own stream
(`workspace.people`, an eventIterator), as #4175 streams a transcript: the
procedure counts the streams open of each workspace, has the host follow it
afresh as each opens, so the window hears where they stand, and lets it go
with the last. The host follows each workspace (WORKSPACES_PEOPLE_FOLLOW,
polled while Realtime fails it) and tells every change, or why there is
none, as WORKSPACE_PEOPLE_CHANGED, never through app state, which the voice
window reads too. A web tab serves the same procedure with nothing of its
own.

The members read stays as the first read; the re-read whenever the
workspace's member count changed, and the settings page's read before
Transfer ownership, are gone: a member joining, leaving, or given another
role elsewhere is pushed.

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
luke-agent Bot added a commit that referenced this pull request Oct 21, 2026
…hat draw them from Neon Realtime

A workspace's members, and who its work names but left, are now followed
over Realtime while a window draws them, on the act port's own stream
(`workspace.people`, an eventIterator), as #4175 streams a transcript: the
procedure counts the streams open of each workspace, has the host follow it
afresh as each opens, so the window hears where they stand, and lets it go
with the last. The host follows each workspace (WORKSPACES_PEOPLE_FOLLOW,
polled while Realtime fails it) and tells every change, or why there is
none, as WORKSPACE_PEOPLE_CHANGED, never through app state, which the voice
window reads too. A web tab serves the same procedure with nothing of its
own.

The members read stays as the first read; the re-read whenever the
workspace's member count changed, and the settings page's read before
Transfer ownership, are gone: a member joining, leaving, or given another
role elsewhere is pushed.

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
luke-agent Bot added a commit that referenced this pull request Oct 21, 2026
…hat draw them from Neon Realtime

A workspace's members, and who its work names but left, are now followed
over Realtime while a window draws them, on the act port's own stream
(`workspace.people`, an eventIterator), as #4175 streams a transcript: the
procedure counts the streams open of each workspace, has the host follow it
afresh as each opens, so the window hears where they stand, and lets it go
with the last. The host follows each workspace (WORKSPACES_PEOPLE_FOLLOW,
polled while Realtime fails it) and tells every change, or why there is
none, as WORKSPACE_PEOPLE_CHANGED, never through app state, which the voice
window reads too. A web tab serves the same procedure with nothing of its
own.

The members read stays as the first read; the re-read whenever the
workspace's member count changed, and the settings page's read before
Transfer ownership, are gone: a member joining, leaving, or given another
role elsewhere is pushed.

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
luke-agent Bot added a commit that referenced this pull request Oct 21, 2026
…hat draw them from Neon Realtime

A workspace's members, and who its work names but left, are now followed
over Realtime while a window draws them, on the act port's own stream
(`workspace.people`, an eventIterator), as #4175 streams a transcript: the
procedure counts the streams open of each workspace, has the host follow it
afresh as each opens, so the window hears where they stand, and lets it go
with the last. The host follows each workspace (WORKSPACES_PEOPLE_FOLLOW,
polled while Realtime fails it) and tells every change, or why there is
none, as WORKSPACE_PEOPLE_CHANGED, never through app state, which the voice
window reads too. A web tab serves the same procedure with nothing of its
own.

The members read stays as the first read; the re-read whenever the
workspace's member count changed, and the settings page's read before
Transfer ownership, are gone: a member joining, leaving, or given another
role elsewhere is pushed.

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
luke-agent Bot added a commit that referenced this pull request Oct 21, 2026
…hat draw them from Neon Realtime

A workspace's members, and who its work names but left, are now followed
over Realtime while a window draws them, on the act port's own stream
(`workspace.people`, an eventIterator), as #4175 streams a transcript: the
procedure counts the streams open of each workspace, has the host follow it
afresh as each opens, so the window hears where they stand, and lets it go
with the last. The host follows each workspace (WORKSPACES_PEOPLE_FOLLOW,
polled while Realtime fails it) and tells every change, or why there is
none, as WORKSPACE_PEOPLE_CHANGED, never through app state, which the voice
window reads too. A web tab serves the same procedure with nothing of its
own.

The members read stays as the first read; the re-read whenever the
workspace's member count changed, and the settings page's read before
Transfer ownership, are gone: a member joining, leaving, or given another
role elsewhere is pushed.

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
github-merge-queue Bot pushed a commit that referenced this pull request Oct 21, 2026
…hat draw them from Neon Realtime (#3942)

A workspace's members, and who its work names but left, are now followed
over Realtime while a window draws them, on the act port's own stream
(`workspace.people`, an eventIterator), as #4175 streams a transcript: the
procedure counts the streams open of each workspace, has the host follow it
afresh as each opens, so the window hears where they stand, and lets it go
with the last. The host follows each workspace (WORKSPACES_PEOPLE_FOLLOW,
polled while Realtime fails it) and tells every change, or why there is
none, as WORKSPACE_PEOPLE_CHANGED, never through app state, which the voice
window reads too. A web tab serves the same procedure with nothing of its
own.

The members read stays as the first read; the re-read whenever the
workspace's member count changed, and the settings page's read before
Transfer ownership, are gone: a member joining, leaving, or given another
role elsewhere is pushed.

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>

This branch was successfully deployed

1 active deployment
Preview — 7f367bd1 Deployed Oct 20, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant