@promptjuggler/browser is the client for streaming: it opens the
SSE connection, speaks the whole protocol — segments, resets, resume, staleness — and
hands you one maintained text per run. Everything in the package is safe to ship to an
end user’s browser; it authenticates with a thread-scoped credential and never sees your
API key.
Installation
fetch + ReadableStream) and in Node.js 22+.
The credential flow
Your backend mints the credential with a server SDK and returns it verbatim; the browser hands it togetToken. That one function is the entire auth story:
Your backend (Node example)
Browser
Events
The one flag that matters is
gapped: when a terminal event carries gapped: true,
the streamed text is (or may be) incomplete — fetch the run through your backend for the
authoritative result. When it is false, the text you rendered is the full output and no
fetch is needed — with one edge for raw-event consumers: a gap arriving after a clean
done retroactively proves that text incomplete (as in the sample above). The React hook
folds all of this into runs[runId].gapped, which is why it is the recommended surface.
Reconnection is automatic — exponential backoff after failures, immediate when the
server hands the connection off during a deploy — and resumes exactly where it left off.
disconnect() stops everything.
Connecting after the run started
A new connection starts at the live tip of the thread, so the usual order is to connect first and trigger the run second. When your frontend only learns the thread from the run it triggered, setreplay: true:
done or failure event, so handle only the runs you show.
React
runs[runId] is { text, data?, transcript?, channel, status: 'streaming' | 'done' | 'failed', gapped?, error? } — render text and you have a chatbot. Render
transcript instead when the UI shows tool activity: it is the
same run as an ordered sequence of prose, tool chips and emit payloads, in the same shape
getPromptRun returns. The options object is read through a ref, so inline literals never
cause reconnects; only the deps array does.
Angular
The same surface as signals, for Angular 17+:stream.connected and stream.runs are signals holding the same shapes as the React
hook. Reactivity follows Angular’s grain: any signal read during the factory call —
here threadId, hoisted into a local — is tracked, and changing it drops the old
subscription (and its runs) and connects a fresh one. Reads deferred into getToken
happen after tracking ends, so hoist them as above. Call it in an injection context
(a field initializer or constructor); the subscription disconnects when the component is
destroyed. Signals-only, so it works in zoneless applications.
Workflows
Every prompt node of a workflow streams into the same connection, keyed byrunId and
tagged with its channel. Follow only the
user-facing lanes with channels: ['support'].
See the chatbot guide for the full end-to-end walkthrough.