Repository navigation
feat(service): the service's held reads and waits on a row are woken by Neon Realtime - #3266
Closed
charleslpan wants to merge 14 commits into
Closed
charleslpan wants to merge 14 commits into
charleslpan wants to merge 14 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
luke-agent
Bot
changed the base branch from
main
to
luke/neon-realtime-scope-inbox
October 17, 2026 01:07
This was referenced Oct 17, 2026
Closed
luke-agent
Bot
force-pushed
the
luke/neon-realtime-scope-inbox
branch
from
October 18, 2026 19:21
0a6e6cd to
35d1920
Compare
luke-agent
Bot
force-pushed
the
luke/neon-realtime-service-wake
branch
from
October 18, 2026 19:40
6c9622d to
f835f49
Compare
luke-agent
Bot
force-pushed
the
luke/neon-realtime-scope-inbox
branch
from
October 18, 2026 21:12
35d1920 to
ff729c0
Compare
luke-agent
Bot
force-pushed
the
luke/neon-realtime-service-wake
branch
from
October 18, 2026 21:40
f835f49 to
2180711
Compare
luke-agent
Bot
force-pushed
the
luke/neon-realtime-scope-inbox
branch
from
October 19, 2026 18:52
ff729c0 to
7c1c859
Compare
luke-agent
Bot
force-pushed
the
luke/neon-realtime-service-wake
branch
from
October 19, 2026 18:52
2180711 to
e46eaf4
Compare
This was referenced Oct 19, 2026
feat(service): the voice brain follows a spoken ask's turn on Neon Realtime instead of polling
#3937
Closed
luke-agent
Bot
force-pushed
the
luke/neon-realtime-scope-inbox
branch
2 times, most recently
from
October 20, 2026 01:29
a7cb527 to
39ca561
Compare
luke-agent
Bot
force-pushed
the
luke/neon-realtime-service-wake
branch
from
October 20, 2026 01:36
e46eaf4 to
b3338fd
Compare
…eon Realtime A plan's inbox and one agent's inbox, the window's held reads, are now answered from a Realtime subscription the host holds while a window reads them, instead of the service re-reading the store every half second: - The service seals one live statement per scope, its open items and its latest settled ones as one result, so an item settling moves in one change (GET /api/live/plan-inbox?id=, /api/live/agent-inbox?id=), after reading the plan or agent as the account, so one it does not reach is not found. - The follower is now generic (live-follow.ts): every part subscribed on one socket and renewed before it expires, its rows decoded by the caller. - The host keeps a subscription per scope while it is read and 30 seconds after, and answers the window's read as soon as the items move past its cursor, or unchanged when the hold ends, as the held read did. Where the service seals nothing, the held read answers as before. Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
…d does Main's held read of a task's or an agent's inbox now says who started each item, its answer's files, its thread, when it settled, and whether it is unread for the reader. The scope's live statement selects the same columns (the store's own `startedByName`, `ANSWER_FILES`, `MESSAGES`, `SETTLED_AT`, and `unreadFor`), and live-wire.ts writes each row as the held read writes its item. The account inbox's two chat parts are followed by the generic follower too. The PGlite parity test now covers a teammate's message with a file, which leaves the item read for them and unread for its starter. Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
…ecks Main's checks refuse a plan-spelled name for a task and a `node:` import anywhere the host's browser doors reach. The task inbox's live route is `/api/live/task-inbox` (its file `routes/live/task-inbox.ts`), and the host digests a scope's cursor with Web Crypto's SHA-256 rather than `node:crypto`. Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
…th no held fallback Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
…ity Neon Realtime requires mutableFunctionsOf makes the statement a temporary view, its placeholders written as literals, and reads every function, aggregate, window function, and operator's function its rule's tree names from pg_rewrite, answering each whose provolatile is not 'i'. pg_depend records no dependency on a built-in, so it cannot answer this. Any branch's live statements can be held to it under getTestDB(). Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
…ly immutable functions Neon Realtime refuses a statement calling any function Postgres does not mark immutable. The plan and agent inbox's one live statement aggregated each item's thread and files with jsonb_agg and jsonb_build_object, both stable, and read its thread's and settlement's instants with extract from a timestamptz, also stable. The thread's latest messages and the files an answer or a message carried are now live parts of their own, which the reader joins to the items (inboxOfLiveRows), and every instant is read at UTC through liveSelection. The held read keeps its aggregates. A test holds every statement the live endpoints seal to immutability (mutableFunctionsOf); it named extract, jsonb_agg and jsonb_build_object on the task and agent parts before this. Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
… read wakes its held read The service's held read stamps whether each item is unread for the reader, so their marker moving wakes it. The live scope inbox's cursor left unread out, so a read or unread mark answered nothing until the hold ended. Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
…leAdapter()
Neon's guide seals a Drizzle select through its Drizzle adapter rather than
raw SQL over toSQL(). LiveStatement now carries the select it was rendered
from, and the sealer passes it to createRealtime({ adapter: drizzleAdapter() })
.seal({ query }), which also refuses a select whose key is not the name
Postgres answers it under. A statement written as text alone, as the tests'
stand-ins are, or one binding an instant, which the adapter does not encode,
is still sealed with rawSql.
@neon/realtime-drizzle 0.1.0 declares drizzle-orm >=0.45.2 <0.46.0 as its
peer; it reads only what 1.0.0-rc.4 builds the same way, so the peer is
allowed in pnpm-workspace.yaml and the version pinned.
Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
A cast through a type's text form (CoerceViaIO) names no function in the rule's tree, so max(at)::text and text::timestamptz, which call the stable timestamptz_out and timestamptz_in, passed. Each cast is now read from the tree: the input function of the type it casts to, and the output function of the type it casts from, a scalar subquery's by its one column; a cast from a type the tree does not say is answered as one. Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
…th asks Main's held read of an inbox item (#3992) now carries its v4 messages without the ones that ask or answer, its whole thread with each ask as it stands on the item, who closed it, and the answer's files from the message that answered. The live scope inbox delivers every message either thread draws, with its ask and answers, and the files of the answering message, and the reader builds both threads and each ask's standing as the held read does. The ask's standing (standingAsk, askStillAsked, knownAsk) moves into @luke/features/inbox-wire, so the store and the host read it one way. Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
luke-agent
Bot
force-pushed
the
luke/neon-realtime-scope-inbox
branch
from
October 20, 2026 01:43
39ca561 to
40e1669
Compare
…by Neon Realtime Every wait of the service's on a row now watches that row over Realtime rather than re-reading it on a clock, so the held routes released desktops still poll, and the service's own waits, stop costing a look every half second: - LiveWake (live/live-wake.ts): one Realtime direct server per function instance, subscribed to a wait's statements for its length; every update wakes it, an error hands it back to its poll. Polling where the deployment has no Realtime, and in every test. - held-read.ts gains lookedUntil: look, then wait for a change of the watched statements, or a 5 s safety beat, or the poll unwatched. heldUntilMoved is built on it. - The account, plan, and agent inbox reads watch their live statements; plan comments, look_at_board, and run_on_mac's wait for a result watch small statements of their own (planCommentStampsLiveStatement, boardImageLiveStatement, macCommandSettledLiveStatement), which carry no words, image, or output. Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
… fallback Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
luke-agent
Bot
force-pushed
the
luke/neon-realtime-service-wake
branch
from
October 20, 2026 02:06
b3338fd to
a768591
Compare
…es it to error Neon's docs: an error is final, so a long-running worker watches for it with onStateChange and subscribes again, on a new client where the connection itself failed. The safety beat stays the backstop. Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
luke-agent
Bot
force-pushed
the
luke/neon-realtime-scope-inbox
branch
2 times, most recently
from
October 20, 2026 15:50
ff97170 to
9710e65
Compare
Contributor
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is part of making Luke as realtime as possible on Neon Realtime and removing hand-rolled freshness logic. It is stacked on #3232 (which is stacked on #3217), and its base is set to #3232's branch.
The desktop-side PRs move the desktop off the held long-polls. This one is the service side. The held routes that released desktops still poll, and the service's own waits on a row, now wait on Realtime instead of re-reading the store every 500 ms or 250 ms.
What changes
live/live-wake.ts:LiveWake.Context.Referencewith no polling default: unprovided, every watch dies. The web runtime providesliveWakeFromEnvironment, using the sameNEON_REALTIME_URL/NEON_REALTIME_SECRETasLiveSealing; missing config is logged as an error and every watch fails loudly. Tests provide a scripted wake (testing/live-wake.ts).http/held-read.ts:lookedUntil(look, hold, live)looks, then waits for the next change of the watched statements or a 5 s safety beat. It takes no poll.heldUntilMovedis built on it.inbox-page.ts) watch the live statements from feat(service,host): the account inbox is followed over Neon Realtime #3217 and feat(service,features,host): a plan's inbox and an agent's are read through the central live layer #3232.plan-comments-app.ts),look_at_board(board-look.ts), andrun_on_mac's wait for a result (mac-command.tsawaitedResult) watch small statements of their own. These carry no words, image, or command output:planCommentStampsLiveStatement,boardImageLiveStatement,macCommandSettledLiveStatement.Transcripts, the planning conversation, and the Mac's own held read are left to their own stacks, which other agents are working on.
Tests
live-wake.test.tschecks four things:planCommentStampsfor a member, a teammate, and an outsider;pnpm check:affected: typecheck, lint, knip, and discarded-effect pass, and all tests pass exceptapps/desktop/src/main/app-menu.test.ts, which can't load Electron in the sandbox. The web bundle tests pass.Realtime is on in production and its variables are in Vercel, so the service's waits follow over Realtime alone, with no polling fallback.
Made with Luke
Summary by cubic
The service's held reads and waits on a row are now woken by Neon Realtime instead of re-reading the store on a poll, so waits like the inbox, plan comments, look_at_board, and run_on_mac's result arrive when the rows change rather than on a clock. Deployments without Realtime, and any watch that breaks or is refused, keep polling as before.
LiveWake, a runtime-provided context that subscribes each wait's statements over Neon Realtime's direct server for the length of the wait; every update wakes it and an error hands it back to its poll.lookedUntilinheld-read.ts, which looks, then waits on the watched statements or a 5-second safety beat;heldUntilMovedis built on it.planCommentStampsLiveStatement,boardImageLiveStatement,macCommandSettledLiveStatement) that carry no words, image, or command output, so only freshness signals travel through Realtime and every answer is still read by the service's own statement.Written for commit 6c9622d. Summary will update on new commits.