@entropy-softworks/chat-client
v2026.8.14
Published
Transport for Entropy Softworks chat: conversations, messages, and the server-sent-event stream, over cookie or bearer authentication
Readme
@entropy-softworks/chat-client
The transport for Entropy Softworks chat: conversations, messages, and the server-sent-event stream, over either authentication scheme wheatley accepts.
It exists because there were about to be two copies. wheatley's own clients had this code in
clients/shared/src/chat/; konsole's assistant dock became the second consumer when konsole deleted
its own inference proxy and started calling wheatley directly. The estate rule is that a second
consumer moves the code rather than copying it.
Installation
npm install @entropy-softworks/chat-clientUsage
Same origin — wheatley's own clients, cookie session plus the CSRF echo:
import { createChatClient } from "@entropy-softworks/chat-client";
const chat = createChatClient({
baseUrl: "/api",
auth: { mode: "cookie", csrfToken: () => readCsrfCookie() },
});
const conversations = await chat.listConversations();
await chat.sendMessage(conversations[0].id, "What broke last night?", {
onDelta: (text) => append(text),
onDone: ({ finish_reason }) => finish(finish_reason),
onError: (code, message) => show(message),
});Another origin — konsole's dock, bearer token from the handoff flow:
import {
createChatClient,
readHandoffCode,
redeemHandoff,
signInUrl,
} from "@entropy-softworks/chat-client";
// 1. No token yet: send the browser through wheatley's own sign-in.
window.location.assign(signInUrl(wheatleyBaseUrl, window.location.origin));
// 2. Back on our page, the code is in the fragment. Redeem it once, then clean the URL.
const handoff = readHandoffCode(window.location.hash);
if (handoff) {
const session = await redeemHandoff(wheatleyBaseUrl, handoff.code);
store(session.session_token);
history.replaceState(null, "", window.location.pathname + window.location.search + handoff.rest);
}
const chat = createChatClient({
baseUrl: wheatleyBaseUrl,
auth: { mode: "bearer", token: () => stored() },
});Why the auth scheme is a parameter
The two are not preferences, they are consequences of where the page is served from. A same-origin
client gets an HttpOnly cookie — unreadable by script, and therefore requiring a CSRF echo on
writes, because the browser attaches it to any request to the origin including one a third-party
page caused. A cross-origin client cannot use that cookie at all (SameSite=Lax), so it holds a
bearer token, and needs no CSRF echo because a cross-site page cannot read its storage to set the
header.
What it is not
No React, no components, no state management. Rendering belongs to @entropy-softworks/chat-ui,
which is a separate package for a separate phase — this one is the wire and stays usable from a
script, a worker, or a test.
License
MIT.
