feat(sdk): chat sessions, host event feed, and host workspaces again
Summary
Re-adds host routing that #7564 removed. I could not find a reason for the removal in that PR, its commits or its reviews. The removed code used the same API-key-to-JWT exchange this PR uses.
The SDK can now start and drive agent chat sessions, and receive a host's live events. This lets an external client such as Roster create a session in a workspace, follow its status, send messages, answer approvals and read the transcript, without the desktop app.
New API
client.chat:createSession({ workspaceId, harness, modeId?, modelId?, resumeHarnessSessionId? })listSessions,retrieveSession,listItemsprompt(takes a plain string or content blocks)cancelTurn,respondToApproval,setMode,setConfigOption,closeSessionsubscribe({ sessionId, since?, deltas?, onEnvelope }): the session's live stream
client.events.subscribe({ onChatSessionChanged?, onEvent? }): the host's/eventsfeed.client.hosts: brings back what #7564 removed when it made the SDK cloud-only. It is a separate namespace, so the cloudworkspacesAPI is unchanged.hosts.list()hosts.projects.list({ hostId? })hosts.workspaces.list/create/createSession/update/delete, withtagsoncreateandupdate.creategenerates a retryidwhen you omit it.- These call host-service's
/trpcrouter (SuperJSON) through the relay. hostIddefaults to the client's.hosts.listsends the user JWT, not the API key. The API forwards the caller's bearer token to the relay to check presence, so with the key alone every host would showonline: false. It lists only the hosts the key's user is linked to.
- All types are defined in the SDK. Nothing imports the private
@superset/chator@superset/chat-runtimepackages, so the published bundle stays self-contained.
Where calls go
Every chat and events call takes hostId and workspaceId:
- Host (machine running Superset): used when
hostIdis set on the call or on the client. The call goes to{relay}/hosts/{organizationId}:{hostId}. The relay URL comes fromhost.relayEndpoint, falling back tohttps://relay.superset.shlike the CLI. - Cloud workspace: used otherwise. The call goes to the workspace's sandbox through the existing gate ticket (
cloudWorkspace.hostTicket/access).
Chat-v3 is a separate tRPC router at /chat-v3/trpc with no SuperJSON transformer, so these calls send and parse plain JSON ({ result: { data } }).
Auth
The relay accepts only a user JWT checked against /api/auth/jwks. With an sk_… API key, the SDK calls GET /api/auth/token once:
enableSessionForAPIKeysturns thex-api-keyrequest into a session.- The
jwtplugin issues the token.
The SDK caches the JWT until a minute before its exp, and drops it after a failed host call. Only the JWT goes to the relay. A credential that is not an sk_ key is already sent as a bearer token, so it goes to the relay as is.
WebSockets use the global WebSocket (Node 22+, Bun, Deno, browsers), or the new WebSocket client option. The token goes in ?token=, which both the relay and the gate accept.
Depends on
- #8168 for
chat:session-changed.ChatSessionChangedEvent.removedmatches that PR's current shape. Hosts without it never send the event, so callers should keeplistSessionsas a fallback.
Test plan
-
bun testinpackages/sdk: 10 pass, 7 of them new. The twohoststests cover:hosts.listsends the JWT, not the API keyhosts.workspaces.createreaches{relay}/hosts/{org}:{host}/trpc/workspaces.createwith a retry id and tags
The five chat and events tests cover:
- host calls trade the key for one JWT and reach the relay as plain JSON
- a bearer credential is passed through
- cloud routing goes to
/chat-v3/trpcon the gate - a call with no target is refused before any request
events.subscribeopenswss://…/hosts/{org}:{host}/events?token=…and routeschat:session-changed
-
bun run typecheckandbun run buildinpackages/sdk: clean. -
bunx biome check(repo): clean.packages/sdk/srcis excluded by config. -
Not run against the live API and relay. The API key to JWT exchange is checked by reading better-auth 1.6.22 (
@better-auth/api-keybefore-hook andjwt/token), not by a live call. -
Not bumped: the SDK version, and
apps/docs/content/docs/sdk/reference.mdx. That page already describes a different SDK surface than this package.
Summary by cubic
The SDK can now start and drive agent chat sessions and receive a host's live events, letting an external client like Roster work without the desktop app.
Adds client.chat (create, list, retrieve, prompt, cancel, approve, set mode and config, close, and live subscribe), client.events.subscribe for the host event feed, and client.hosts for listing host machines and managing their projects and workspaces. Calls target a host through the relay when hostId is set, otherwise the named cloud workspace's sandbox. The relay accepts only a user JWT, so API keys are traded once for a JWT at /api/auth/token and cached until near expiry.
Details
- Chat calls use a new
/chat-v3tRPC router with plain JSON instead of SuperJSON. - WebSockets use the global
WebSocket; older runtimes pass one via theWebSocketclient option. hosts.listsends the JWT instead of the API key so the API can check host presence at the relay.- Depends on #8168 for
chat:session-changed; hosts without it never send the event, so keeplistSessionsas a fallback.
Summary by CodeRabbit
- New Features
- Added SDK support for listing hosts and projects, and creating, updating, listing, and deleting host workspaces.
- Added support for creating and managing chat sessions, sending prompts, handling approvals, and receiving live chat updates.
- Added event subscriptions for chat-session changes, with callbacks for messages, errors, and connection closures.
- Added host-targeted and cloud-workspace routing, including relay authentication and configurable WebSocket connections.
- Documentation
- Expanded the SDK guides with host, workspace, chat, event subscription, authentication, routing, and WebSocket setup details.