@dreameros/sdk
v0.1.1
Published
DreamerOS SCS Gateway client SDK. Typed access to the verified chat, streaming, and memory endpoints.
Maintainers
Readme
@dreameros/sdk
Early version. Published to npm as
@dreameros/sdkv0.1.0 (live since 2026-06-18). This is a pre-1.0 surface: minor versions may include breaking changes until 1.0. Pin an exact version if that matters to you.
A dependency-free TypeScript client for the DreamerOS SCS Gateway. It wraps the
gateway's verified HTTP surface: non-streaming governed chat, the governed
event stream, the memory write/recall endpoints, and multi-engine routing. It
runs on Node 22 and in modern browsers using the global fetch and the web
ReadableStream.
Install
npm install @dreameros/sdkQuick start
import { DreamerOSClient } from "@dreameros/sdk";
const client = new DreamerOSClient({
// Optional. Defaults to the production gateway.
baseUrl: "https://dreameros-scs-gateway-production.up.railway.app",
apiKey: process.env.DREAMEROS_API_KEY as string,
});Authentication
The token you pass as apiKey is sent as Authorization: Bearer <token> on
every request. The gateway uses two different auth paths, so the token you need
depends on the method you call:
rememberandrecall(the/api/v1/actions/*endpoints) accept a DreamerOS MCP API key in the formdros_*. Generate one withPOST /api/v1/api-key/generate. These endpoints also accept the key as a?api_key=dros_*query parameter; this client sends it as a Bearer header.chatandchatStream(/api/v1/chatand/api/v1/chat/stream) validate a gateway-issued JWT or a Supabase session token from the Bearer header. They do NOT accept adros_*MCP key.
If you call both surfaces from one process, construct one client per token.
Non-streaming chat
const res = await client.chat({ message: "Summarize today's deploy log." });
console.log(res.content); // the assistant answer
console.log(res.follow_ups); // suggested follow-up prompts
console.log(res.governance_metadata);chat() returns the whole governed response: content plus the governance
pack (DAIM statuses, detected intent, follow-ups, verification flags, and the
rest). See ChatResponse for the typed fields.
Streaming chat
chatStream() returns an async iterable of typed events. Branch on
event.type:
let answer = "";
for await (const event of client.chatStream({ message: "Explain the SCS header." })) {
switch (event.type) {
case "meta":
console.log("conversation:", event.conversation_id);
break;
case "ede_diff":
console.log("restructured prompt:", event.restructured);
break;
case "governance_step":
console.log("step", event.step, "->", event.status);
break;
case "token":
answer += event.content;
break;
case "metadata_final":
console.log("follow-ups:", event.follow_ups);
break;
case "error":
console.error("stream error:", event.message);
break;
case "received":
console.log("gateway has the message, working on it");
break;
case "thought":
console.log("live status:", event.text);
break;
default:
// Unknown event types are yielded for forward compatibility; ignore them.
break;
}
}
console.log(answer);A note on "streaming": today the gateway delivers the assistant body as a
single token event near the end of the stream, alongside the per-step
governance pills (governance_step) and the final metadata_final pack. True
token-by-token streaming is forthcoming; when it lands it will arrive as
additional token events and this loop needs no change.
Memory: remember and recall
// Write a memory entry. Only `content` is required.
await client.remember({
content: "Decided to ship the SDK package this sprint.",
memory_type: "semantic",
tags: ["sprint-2026-06-18", "cold-start-anchor"],
confidence: 1.0,
});
// Recall memory entries. POST, not GET.
const recalled = await client.recall({
query: "What did we decide about the SDK?",
limit: 10,
});
console.log(recalled);Multi-engine routing
route() sends one message to multiple constellation engines and returns a
single result. POST, like the other actions methods, authenticated with the
dros_* MCP key. This method is Pro-tier and above; a light-tier key receives a
403 from the gateway.
const result = await client.route({
message: "Should we ship the SDK this sprint?",
// Strategy defaults to "best_fit". Other strategies: "consensus",
// "compare", "sequential".
strategy: "consensus",
// Optional explicit engine subset; omit to let the gateway choose.
engines: ["claude", "gemini", "perplexity"],
});
console.log(result);A note on latency: the consensus, compare, and sequential strategies fan
out to several engines and synthesize the replies, so this call can be markedly
slower than a single chat turn. Budget for it.
Authentication, by method
| Method | Token type |
|--------|------------|
| chat, chatStream | JWT / Supabase session token (NOT dros_*) |
| remember, recall, route | dros_* MCP key |
If you call a method with the wrong token type, the client raises a
DreamerOSAuthError before the request fires, with a message naming the token
the method needs. This is a conservative check: only the dros_* prefix
discriminates, so any non-dros_ token is accepted for the chat methods.
import { DreamerOSAuthError } from "@dreameros/sdk";
try {
// A dros_ key cannot call chat(); a session token cannot call recall().
await client.recall({ query: "..." });
} catch (err) {
if (err instanceof DreamerOSAuthError) {
console.error(err.surface, err.message);
}
}Errors
Any non-2xx response throws a DreamerOSApiError carrying the HTTP status,
the parsed body (JSON envelope when available, otherwise raw text), and the
request path:
import { DreamerOSApiError } from "@dreameros/sdk";
try {
await client.chat({ message: "" });
} catch (err) {
if (err instanceof DreamerOSApiError) {
console.error(err.status, err.body);
}
}Public API surface
The client wraps these verified gateway endpoints:
| Method | HTTP | Path | Auth token |
|--------|------|------|------------|
| chat(request) | POST | /api/v1/chat | JWT / Supabase session |
| chatStream(request) | POST | /api/v1/chat/stream | JWT / Supabase session |
| remember(request) | POST | /api/v1/actions/remember | dros_* MCP key |
| recall(request) | POST | /api/v1/actions/recall | dros_* MCP key |
| route(request) | POST | /api/v1/actions/route | dros_* MCP key (Pro+) |
What is intentionally not covered
- The gateway exposes
POST /api/v1/receipts(the Signed Receipts API). It is not wrapped here because it is Elite-tier-only, bills per receipt, and authenticates through the JWT / Supabase user path rather than the developerdros_*key the actions surface uses. Exposing it as a plain SDK method would imply it works with the same token and tier as the rest of this client, which it does not. It may be added behind an explicit, separately-documented method once the developer-facing auth and pricing story is settled. - The other
/api/v1/actions/*tools (forget, space, state, canon, govern, agent, context, import-memories, conversations) are real endpoints but are out of scope for this first release. The client structure makes adding them a small, contract-verified addition.
Forthcoming
- Wider
/api/v1/actions/*coverage (forget, space, state, canon, govern, agent, context, import-memories, conversations) as each is contract-verified. - A separately-documented method for the Signed Receipts API
(
POST /api/v1/receipts) once its developer-facing auth and pricing story is settled, per the note above. - True token-by-token streaming on
chatStream, when the gateway emits the assistant body as incrementaltokenevents; no client change will be required.
License
See the LICENSE file in this package.
