@marrowid/sdk
v1.0.11
Published
Typed Marrow client for evidence-grounded, correctable agent memory.
Readme
@marrowid/sdk
The official TypeScript client for Marrow. It provides one typed surface for source ingest, evidence-backed query, and correctable agent memory in modern Node.js and browsers.
Install
npm install @marrowid/[email protected]Connect and retrieve context
Create a customer API key with only the scopes your application needs, then copy it into the client from secret storage. A customer API key is an application credential; it is not a Console login session or an admin token.
import { Marrow } from "@marrowid/sdk";
const marrow = new Marrow({
apiKey: process.env.MARROW_API_KEY!,
});
const peer = marrow.peer("riley");
const context = await peer.ask("What should this assistant remember?");
if (context.status === "insufficient_evidence") {
throw new Error("Add source material before using Marrow context");
}
const messages = context.toOpenAI();Context.toOpenAI() returns an OpenAI-compatible system-message array.
Context.toAnthropic() returns an Anthropic-compatible system prompt. These
are plain data adapters and do not add provider SDK dependencies.
Every asynchronous method returns a named TypeScript response type. Use
marrow.access() to inspect the active key's scopes, context boundaries,
quotas, and credit balance before starting work.
Ingest sources
Dry runs preview a source without an idempotency key. Live ingest requires a caller-owned key between 8 and 160 characters; reuse it for an exact retry.
const preview = await marrow.ingest.url("https://example.com/onboarding-note");
const queued = await marrow.ingest.url("https://example.com/onboarding-note", {
dryRun: false,
datedAt: "2026-07-15",
idempotencyKey: "onboarding-note-2026-07-15",
});
if (queued.schemaVersion === "marrow-ingest-job-v1") {
const completed = await marrow.ingest.jobs.wait(queued.job.id);
console.log(completed.job.status);
}Browser and Node byte sources use the same file method:
await marrow.ingest.file({
filename: "preferences.md",
contentType: "text/markdown",
content: new TextEncoder().encode("Prefers aisle seats."),
dryRun: false,
datedAt: "2026-07-15",
idempotencyKey: "preferences-file-2026-07-15",
});Write and correct memory
const session = marrow.session("project-update");
const receipt = await session.addMessages(
[{ role: "user", content: "Lead project updates with the decision." }],
{ peerId: "riley", infer: true },
);
const event = await marrow.events.get(receipt.event_id);
if (event.status === "failed" || event.status === "quarantined") {
throw new Error(`Memory event ended with ${event.status}`);
}
const claim = await marrow.claims.get("claim-id");
const corrected = await marrow.claims.update(
claim.id,
"Lead project updates with the decision and its cited source.",
{ headRevision: claim.head_revision, reason: "User clarified the preference." },
);
await marrow.claims.delete(claim.id, {
headRevision: corrected.head_revision,
reason: "User withdrew the preference.",
});Claim corrections preserve history and use the current head_revision as an
opaque concurrency token. A stale revision returns a typed ConflictError.
Delete a source, peer, or session through the same client. Each deletion is asynchronous and returns the existing job or event receipt for polling:
const sourceDeletion = await marrow.sources.delete(sourceId);
await marrow.ingest.jobs.wait(sourceDeletion.job.id);
const peerDeletion = await marrow.peer("riley").delete();
await marrow.events.get(peerDeletion.event_id);Errors and transport
The client maps validation, wrong-credential, missing-scope, not-found, and
conflict responses to typed errors. Malformed success bodies fail closed with
MarrowResponseError. API keys are held in private in-memory fields and are
never included in client JSON or error objects.
The default transport uses the platform fetch. Supply fetch in the
constructor for a compatible runtime, test harness, or controlled proxy. Remote
base URLs require HTTPS; plain HTTP is accepted only for loopback development.
Public surface
new Marrow({ apiKey, baseURL?, workspace?, timeoutMs?, fetch? })marrow.access()marrow.ingest.url()/.file()/.jobs.list()/.get()/.wait()marrow.sources.delete(sourceId)marrow.query()marrow.workspaces.upsert()/.list()marrow.peer(id).create()/.get()/.ask()/.context()/.representation()/.delete()marrow.session(id).create()/.get()/.addMessages()/.messages()/.context()/.delete()marrow.claims.query()/.list()/.get()/.history()/.update()/.delete()marrow.events.get()/marrow.queue.status()
Proprietary. UNLICENSED means this package is a distribution channel, not an
open-source grant.
