Every factual claim in this course traces to something here, grouped by the module it serves. Two things to know about this particular list. Several entries are cited for what a source does not say — those are as load-bearing as the positive claims. And the libraries in this space move fast enough that a version-specific fact should be re-checked rather than trusted; if you find a source that contradicts a lesson, that is worth raising with your teaching agent.
What is actually travelling between the model and the browser, and what carries it there intact.
WhyThe three limits: 0.1s for “the system is reacting instantaneously,” 1.0s for “the user’s flow of thought to stay uninterrupted,” 10s for “keeping the user’s attention focused on the dialogue.” Past ten seconds, “users will want to perform other tasks while waiting… so they should be given feedback indicating when the computer expects to be done.” Primary source for “the wait is the interface.” Note what it does not support: the prescribed fix above ten seconds is a percent-done indicator, which an agent cannot honestly produce — the course’s argument that the guidance runs out exactly there is the course’s own, not Nielsen’s.
WhySplits Nielsen’s middle band into a rendering rule: “use a looped indicator for delays of 2–9 seconds and a percent-done indicator for delays of 10 seconds or more,” and “use a progress indicator for any action that takes longer than about 1.0 second.” The clearest statement of the convention that agentic responses break.
WhyTwo facts the first lesson leans on. On long non-streaming requests: “some networks may drop idle connections after a variable period of time, which can cause the request to fail or time out without receiving a response,” and the SDKs “validate that your non-streaming Messages API requests are not expected to exceed a 10-minute timeout.” And on failure once you stream: “an error can occur after the API returns a 200 response. In that case, error handling doesn’t follow these standard mechanisms.” That second one is the structural change — streaming moves failure out of the status code and into the body. 529 overloaded_error is the mid-stream error the streaming page shows as an example.
Also cited in Control’s retry lesson — the SDK’s two-attempt auto-retry covers connection errors, rate limits and 5xx failures to get a response started, not a death at token four hundred — and Control’s stop-button lesson as a negative finding: nothing here addresses billing for a client-initiated disconnect. Relisted again in Production for the same two jobs: 529 as the worked mid-stream provider failure in the error-taxonomy lesson, and the same billing silence in the abandoned-run lesson.
WhyThe event sequence (message_start, content_block_start/_delta/_stop, message_delta, message_stop, plus ping and error), the delta types (text_delta, input_json_delta, thinking_delta, signature_delta), the index addressing scheme, and the tool-use contract: deltas are “partial JSON strings, whereas the final tool_use.input is always an object,” accumulated and parsed at content_block_stop. Two details the lessons single out: the warning that message_delta usage counts “are cumulative,” and that models emit “one complete key and value property from input at a time. As such… there may be delays between streaming events” — a documented pause that a naive UI reports as a hang. Primary source for “reading the raw events.”
Relisted in Rendering a partial answer’s “Streaming structure, not text” lesson: the input_json_delta boundary is why nothing before content_block_stop is guaranteed to be an object, and the one-field-at-a-time note is the only in-band settle signal that lesson has.
WhyWhere citations_delta is actually documented — not on the streaming page. “For streaming responses, citations arrive as a citations_delta delta type inside content_block_delta events.” Cited as an example of a delta type a text projection cannot see at all.
WhyIntroduces the Responses API’s semantically named events — response.created, response.output_text.delta, response.completed, error. Note the course’s explicit negative finding: neither this page nor the request-params source mentions a data: [DONE] terminator for the Responses API, and the documented terminator is response.completed. The full reference at platform.openai.com returns 403 to automated fetching, which is why the event inventory below is sourced from the SDK instead.
Also cited in Control’s stop-button lesson and Production’s abandoned-run lesson, both as the same negative finding: neither this page nor Anthropic’s addresses billing for a disconnected or aborted generation.
WhyThe event union, in code rather than prose: dozens of classes, including response.function_call_arguments.delta, whose delta is “the function-call arguments delta that is added” — the same accumulate-then-parse burden as Anthropic’s input_json_delta. Worth knowing the naming trap: the text event’s class is ResponseTextDeltaEvent but its wire type is response.output_text.delta.
WhyThe one place the [DONE] claim is verifiable for Chat Completions: “an additional chunk will be streamed before the data: [DONE] message.” The reason the lesson says “Chat Completions” rather than “OpenAI” — the answer differs by endpoint within one vendor.
Relisted in Production’s error-taxonomy lesson, which identifies a dropped transport by the absence of a terminal event and needs to know precisely which terminator belongs to which endpoint.
WhyThe wire format (event, data, id, retry; blank-line delimiter; “a colon as the first character of a line is in essence a comment, and is ignored”), automatic reconnection — “by default, if the connection between the client and server closes, the connection is restarted” — and the connection limit: six per browser and domain outside HTTP/2, while “when using HTTP/2, the maximum number of simultaneous HTTP streams is negotiated between the server and the client (defaults to 100).” Primary source for “choosing a transport.” What it does not say: that EventSource is GET-only or cannot send headers or a body. MDN states only that the channel is unidirectional.
Relisted in Production’s infrastructure lesson for its connection-limit paragraph, read there against RFC 9113’s HTTP/2 stream-limit text — and for the comment-line syntax, which is the keep-alive that defeats every between-bytes timeout in that module at once.
WhyWhere the GET-only property actually comes from: the EventSource() constructor creates a “potential-CORS request,” which sets no method, headers or body, and Fetch defines a request’s method as GET “unless stated otherwise.” Also the only source for the Last-Event-ID mechanism — MDN has no page for that header, it 404s. Note the spec gives no default retry interval: it is “implementation- defined,” so the widely-quoted three seconds is a browser detail, not a documented constant.
Relisted in Production’s two-tabs-one-run lesson for the reattachment cursor rather than the GET-only constraint: the id: field sets the last event ID, and on reconnection the browser sets Last-Event-ID in the request.
WhyBidirectionality, the constructor, and the sentence the transport table quotes about the interface not supporting backpressure. Read with Writing WebSocket servers for the handshake (GET + Upgrade: websocket → 101, and the SHA-1 magic string) and the protocol-upgrade guide for “HTTP/2 explicitly disallows the use of this mechanism and header.” What MDN does not say: that WebSocket lacks automatic reconnection. It never mentions one and demonstrates manual reconnection — which is a weaker claim than the negative, and the lesson states it that way.
WhyChunked framing (hex length prefix per chunk, zero-length terminator, Content-Length omitted) and the warning that matters most: “HTTP/2 disallows all uses of the Transfer-Encoding header… Usage of the header in HTTP/2 may likely result in a specific protocol error.” The source for separating “streaming” from “chunked.”
Relisted in Production’s infrastructure lesson, paired with RFC 9114’s statement that HTTP/3 does not define transfer codings at all.
WhyHow a client reads a streaming fetch incrementally: the reader loop resolving { done, value }, and the simpler for await...of form over response.body, including MDN’s own AbortSignal-integrated example.
Whyproxy_buffering defaults to on, which is the single most common cause of a stream arriving in one lump. The same directive documents X-Accel-Buffering as the per-response escape hatch and notes it “can be disabled using the proxy_ignore_headers directive.” proxy_read_timeout defaults to 60s and applies “only between two successive read operations, not for the transmission of the whole response” — which is why a keep-alive comment line matters and why a ninety-second quiet tool call gets cut.
Relisted in Production’s infrastructure lesson, where the same two defaults are the obstacle rather than a column to score — including the case where X-Accel-Buffering has already been neutralised by proxy_ignore_headers. That lesson also uses proxy_http_version, which has defaulted to 1.1 only since nginx 1.29.7 (1.0 before that): on an older install the upstream hop has no chunked framing at all, so disabling buffering cannot help.
WhyCited for a negative finding. The universally repeated “set gzip off for SSE” advice is not in nginx’s own docs, which say nothing about streaming or flushing at all — and the defaults make it largely moot (gzip off, gzip_types defaulting to text/html, gzip_min_length measured from a Content-Length a chunked stream does not have). Compression can break streaming; attributing that to nginx is citing a source that does not say it.
WhyResponse Body Buffering: “Standard” (the default) “allows Cloudflare products to inspect a prefix of the response body,” against “None: strictly no buffering.” Inspecting a prefix is enough to stall a stream’s first tokens. The changelog announcing it names the price of turning it off: “may break security functionality that requires body inspection, including the Web Application Firewall (WAF) and Bot Management.”
Relisted in Production’s infrastructure lesson, read beside the Azure Application Gateway entry: both vendors buffer on purpose, both state a real benefit, and in both cases the person who owns the setting is not the person who wants it changed.
WhyThe unusually explicit pro-streaming case: “CloudFront supports only the chunked value of the Transfer-Encoding header. If your origin returns Transfer-Encoding: chunked, CloudFront returns the object to the client as the object is received at the edge location.” Also: it forwards to custom origins over HTTP/1.1, strips Upgrade unless a WebSocket connection is established, and its origin response timeout measures the gap between response packets rather than total duration.
Relisted in Production’s infrastructure lesson as the counterexample the lesson needs: a CDN that passes a stream through by design, with the same between-bytes timeout shape as nginx’s proxy_read_timeout. Scope: this is CloudFront specifically and says nothing about Application Load Balancer or API Gateway, whose behaviour this course has not verified.
The vocabulary a turn needs beyond text — tool calls, step boundaries, approvals, and failure.
WhyThe most complete published inventory of what an agent stream carries: start, text-*, reasoning-*, tool-input-start/-delta/-available, tool-output-available, tool-output-denied, the approval pair, start-step/finish-step/reset-step, error, abort, finish, terminating with data: [DONE]. The page states the transport plainly: “the data stream protocol uses Server-Sent Events (SSE) format,” and that a custom backend must set x-vercel-ai-ui-message-stream: v1. Primary source for “an agent is not a text stream.” Treat it as a catalogue of the problem space, not an API to adopt on sight.
WhyCited because it contradicts the docs page above: the shipped Zod schema accepts tool-input-error, tool-output-error and message-metadata, none of which the documentation lists. If you write a backend speaking this protocol, the schema is the authority. The general habit — check the source when the docs are the vendor’s own — is worth more than the specific finding.
Relisted in Production’s error-taxonomy lesson for its tool-failure leg: tool-input-error and tool-output-error give the wire format a first-class slot for “the tool call itself failed,” distinct from the model or the transport failing.
WhyThe five-header defensive set the transport reference reproduces, including x-accel-buffering: no with the source comment “disable nginx buffering.” Source-verified rather than documented — the docs name only x-vercel-ai-ui-message-stream.
WhyThe client-side model: a message holds a parts array rather than a string, with variants for text, reasoning, step-start, files, sources, and tool parts typed as tool-<name>. Read alongside Chatbot Tool Usage for the six-state tool part machine — input-streaming, input-available, approval-requested, approval-responded, output-available, output-error — the states a spinner collapses into two.
WhyThe evidence for this course’s standing “prefer the wire to the wrapper” rule. v7 alone renamed system to instructions, onFinish to onEnd, result.fullStream to result.stream, moved response helpers to standalone functions and dropped CommonJS — with v5 and v6 still separately maintained on their own npm tags and doc hosts. Any tutorial you find is probably about a different major.
WhyCited for what it rules out. Streaming React components from the server (streamUI, createStreamableUI) is not the current recommendation: “AI SDK RSC is currently experimental. We recommend using AI SDK UI for production.” The documented approach is client-side rendering off typed tool parts, which is worth knowing before building on a pattern the vendor has stepped back from.
Almost every usable source for this module is written by Vercel about libraries Vercel ships, so the vendor flag applies to most of the list and is repeated in the lesson prose. The one place the module makes a strong claim — that deciding which parts of an answer may be shown before they are complete is a product decision — has no source behind it at all, and the lesson says so rather than borrowing one.
WhyCited by “Forty percent of a thought” for one structural move and nothing more: streaming converts a decision over a finished object into a decision over a prefix. It is safety-filtering research, not interface research — it does not study users, trust, or disclosure, and it says nothing about recommendations. Recorded here so the boundary is visible: searching arXiv, the NN/g library and the general web for work measuring what a half-finished recommendation does to a reader found adjacent material and nothing equivalent, so the lesson’s central rule is written as the course’s own argument rather than as a finding.
WhyPrimary source for “Streaming structure, not text,” and the reason that lesson is not written against the API every tutorial still uses: “generateObject and streamObject have been deprecated (PR #10754). They will be removed in a future version,” with the guide directing you to “generateText and streamText with an output setting instead.”
WhyThe correction at the centre of “Streaming structure, not text.” The object you render mid-stream is not validated: “Complete output is fully validated against the schema. Partial output (during streaming) is a deep partial version of the schema type,” and more bluntly, “Partial outputs streamed via streamText cannot be validated against your provided schema, as incomplete data may not yet conform to the expected structure.” Also the source for the current Output.object() and partialOutputStream code shape.
WhyThe predecessor API’s reference page, which said the same thing about its own partial stream: values are “typed with a deep partial type, but not validated,” and for certainty that content matches the schema “you need to implement your own validation for partial results, e.g. using Zod.” Two API generations, one unchanged contract. Note the URL slug still says stream-object while the rendered page is now about Output.object() — the old reference was folded into its own replacement, which is itself evidence for this course’s rule about preferring the wire to the wrapper.
WhyPrimary source for “Markdown that is still arriving,” read as a specification of the problem rather than a recommendation of the solution. The named failure cases are “bold text that hasn’t been closed yet, partial code blocks missing their closing backticks, and unterminated links without closing brackets,” plus progressive tables where a row can arrive before its header. The repair engine, remend, is named and not specified — the public docs go no further than “detects and closes unterminated syntax,” and the lesson does not claim more.
WhySource for the useIsCodeFenceIncomplete contract quoted in “Markdown that is still arriving”: true when streaming mode is active, the component is in the last block being streamed, and that block has an unclosed code fence. The scoping to the last streaming block is the usable part — an earlier fence in the same document is finished and should render normally.
WhyCited by “Markdown that is still arriving” as a negative finding, and the reason that lesson does not promise any library makes streamed markdown flicker-free. The vendor that built the dedicated tool for this problem has the problem open in its own tracker, which is what makes the per-block-type treatment decision the reader’s regardless of what they install.
WhyPrimary source for “The honest skeleton.” Its own scale — under 1s nothing, up to 10s a skeleton, past 10s a progress bar — is a different scale from the Nielsen and Sherwin response-time limits already listed under The wire, and the lesson keeps them apart rather than merging them. Also the source for NN/g’s own criticisms: a structure-only skeleton “fails to communicate page structure accurately,” animated skeletons “can potentially be distracting, annoying, or even create accessibility problems,” and skeletons “do not replace performance-optimization efforts” and are “simply one extra tool.” Calling an inaccurate skeleton a lie is the course’s own escalation, argued from how long the inaccuracy stays on screen, and the lesson labels it as such.
Stopping, retrying, resuming and correcting — the parts nobody demos. Two of the four lessons here rest on things a source does NOT say: MDN never states what an abort does to the server, and neither Anthropic nor OpenAI documents an idempotency mechanism for a retried generation or what an abandoned one costs. Those absences were searched for deliberately on September 5, 2026 and are cited as findings. Everything about what a backend does with a disconnect comes from Vercel, describing its own default wiring.
WhyPrimary source for the stop-button lesson, cited for one documented fact and one silence. The fact: abort() “is able to abort fetch requests, the consumption of any response bodies, or streams” — every verb describing the client, and the default reason is an AbortError DOMException. Companion pages cover AbortSignal.timeout() (aborts with a distinct TimeoutError, is paused in bfcache, and cannot be cancelled) and AbortSignal.any(), whose own example is a cancel button combined with a timeout. The silence: none of those three pages, nor the DOM Standard’s abort section, says whether aborting stops the server producing — the four were the search, and the absence is the lesson’s premise, not a gap to fill in. Relisted in Production’s abandoned-run lesson for the same silence.
WhyWhere AbortController and AbortSignal are normatively defined, and where the stop-button lesson gets the scope of the mechanism: abort() “toggles the state of a corresponding AbortSignal object. The API which wishes to support aborting can accept an AbortSignal object, and use its state to determine how to proceed.” The conformance list in §3.3 binds “any web platform API using promises,” and the spec’s own illustrative abort step reads “Stop doing amazing things” — the amazing things being in the same agent. Cited for what the standard requires; MDN is cited separately for observed behaviour.
WhyOne line of IDL, load-bearing for the stop-button lesson’s server half: readonly attribute AbortSignal signal on Request. A route handler receiving a Request therefore has a signal to observe. Note the limit of what this establishes — the interface exists; whether a given server runtime aborts that signal when a client disconnects mid-response is runtime behaviour, not spec, and was not verified here for any specific runtime.
WhyPrimary source for the refresh lesson and the load-bearing quote in the stop-button lesson: “client-side aborts are treated as disconnects. Closing a tab, refreshing the page, or calling stop() only closes the current HTTP connection and should not cancel the underlying generation.” That is Vercel describing its own default wiring, not a law of streaming HTTP, and “should not” is intent rather than guarantee. Also the price list for resumability: Redis, a persistence layer tracking activeStreamId per chat, two endpoints, and the resumable-stream package. Redis is the vendor’s choice; the requirement is any durable store keyed by a stream id and readable by a process other than the generating one.
Relisted in Production’s two-tabs-one-run lesson (the same architecture, restated as “a durable, queryable store keyed by a stream id”) and abandoned-run lesson (the same quote, with the caveat that it describes the SDK’s default wiring rather than a universal law — a backend that forwards the disconnect into a cancellation does stop generation).
WhyPrimary source for the partially-wrong-answer lesson and cited in the retry and refresh lessons. Three parts of the returned API matter here. sendMessage: “if a messageId is provided, the message will be replaced (useful for editing)” — the engineering evidence that correcting and asking are distinct operations. regenerate: “regenerate the last assistant message or a specific message,” which replaces content and is not documented as a mid-stream resume or as safe to call while a stream to that message is in flight. And resumeStream, the client half of the resumable-streams contract. Everything visual — marking a message edited, marking an answer superseded — is absent, which is the point the lesson makes.
WhyPrimary source for the retry lesson, cited as a negative finding. Searched directly for an Idempotency-Key header or any request-level idempotency mechanism and found none documented — and this is the page where it would be. The absence is why a naive retry after a mid-stream death cannot be recognised as the same call by anyone but you. Generic REST advice and Stripe’s idempotency keys are a different vendor and a different product; do not carry the mechanism across by analogy.
The infrastructure in front of the stream, the run that outlives one connection, and the ways it ends badly. Specifications and vendor documentation are held to different standards here on purpose: a spec is a general truth, while nginx, Microsoft, Cloudflare, Vercel, Anthropic and OpenAI are each describing their own product on a particular date. Every platform limit below is attributed to the platform it belongs to, and the limits this module could not verify are named as unverified rather than repeated.
WhyPrimary source for the surviving-the-infrastructure lesson. The load balancer that waits for the whole answer, in its own words: the response buffer “can collect all or parts of the response packets sent by the backend server, before delivering them to the clients,” and “by default, the Response buffering is enabled… which is useful to accommodate slow clients,” so backend connections “can be closed once Application Gateway receives complete response.” The operational details are what make the lesson practical rather than indignant: the setting is resource-level and “can’t be managed separately for each listener,” “currently, these changes aren’t supported through the Azure portal” (CLI, PowerShell or ARM only), and request buffering “can’t be disabled if you’re running the WAF SKU” — though “Response buffering isn’t impacted by the WAF.” Every one of those is a fact about Azure on a date, not about streaming.
WhyCited in the infrastructure lesson for concurrent-stream limits, and specifically for a discrepancy worth carrying. Of SETTINGS_MAX_CONCURRENT_STREAMS the spec says “initially, there is no limit to this value,” and only that “it is recommended that this value be no smaller than 100, so as to not unnecessarily limit parallelism.” MDN’s SSE page describes HTTP/2 as negotiating the limit “(defaults to 100).” A recommendation and a default are different claims; the number is settled between a server and a client, neither of which is your code, so the defensible design assumes the HTTP/1.1 six-connection floor.
WhyTwo facts for the infrastructure lesson. Framing: “Transfer codings… are not defined for HTTP/3; the Transfer-Encoding header field MUST NOT be used” — the strongest statement of why “chunked” is an HTTP/1.1 detail rather than a synonym for streaming. And idle timeouts below anything a proxy config can reach: “if the QUIC connection remains idle (no packets received) for longer than this duration, the peer will assume that the connection has been closed,” with the duration declared at the QUIC handshake. The spec also states that “at least 100 request streams SHOULD be permitted at a time.”
WhyPrimary source for the error-taxonomy lesson, and the only place this course found a refusal modelled as a first-class stream event: response.refusal.delta, “emitted when there is partial refusal text being streamed,” and response.refusal.done carrying the finalised text, with the content part typed as either ResponseOutputText or ResponseOutputRefusal. A refusal is a normal completion in which the model declined, which is why retrying it is wrong and an error state is inaccurate. Scope this tightly: it is OpenAI’s Responses API. Anthropic’s streaming and errors pages name no equivalent refusal event, so on that provider a refusal arrives as ordinary text content and is not machine-detectable. Do not generalise the mechanism to “providers.”
Every claim on these pages links to its source. If a source looks wrong or out of date, check the resource list and tell your teaching agent — the course is meant to be corrected.