feat(service,host): a sent message is confirmed once its row reaches the live transcript

Draft
CH
5d ago
+206-30

A small follow-up so the agent chat feels smoother, stacked on #4181. It follows Neon's guide to optimistic mutations (https://neon.com/docs/ai/skills/neon-realtime/references/mutations.md): the write returns its transaction id, and the client waits for that transaction on its live subscription before it confirms.

What changed

  • Database (writer.ts): the message insert's RETURNING also reads pg_current_xact_id()::text, and writeAwaitingLine answers that txid when it wrote the row. No extra round trip is needed. A line that was already standing under its key answers no txid.
  • Service:
    • steerCodingAgentWritten answers the outcome beside the txid, and steerCodingAgent keeps its answer for every other caller.
    • The send route (POST /api/agents/{id}/messages) answers { agent, txid? }.
    • The new wire schemas are codingAgentMessageAnswerSchema and codingAgentMessageAnswerViewSchema, recorded in hosted-wire-schemas.test.ts with goldens generated under LUKE_UPDATE_FIXTURES=1. Released desktops ignore the extra key.
  • Host:
    • live-follow.ts hands each part's subscription to an optional onSubscribed, and the transcript follow keeps its messages part.
    • Before codingAgents.message confirms, it calls awaitTranscriptTxId. Where a window follows that agent's transcript, this calls awaitTxId(txid, 10_000) on the messages subscription.
    • The wait is skipped where no window follows the transcript, or where the send answered no txid.
    • A timeout or error still confirms the send.
    • The txid stays in the host; the window's answer is unchanged.

Tests

  • coding-agent-store.test.ts: a written line answers a decimal txid, and a repeat answers none.
  • compose-coding-agents.test.ts: with no transcript followed the send confirms at once. With one followed, the host waits on the messages subscription for the send's txid.
  • The send tests in coding-agents-app.test.ts read the answer with the new schema.

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
  • tsc for database, features, host, service and web
  • vitest for features, host, the service's coding-agents-app (194 tests), and the store test

What to check by hand

Send an agent a message while its tab is open. The line appears at once and stays put, with no flicker or jump when the service's row replaces it.

Made with Luke