npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@deyta-ai/sdk

v0.6.0

Published

TypeScript SDK for the Deyta platform — memory, namespaces, integrations, and personas.

Readme

@deyta-ai/sdk

TypeScript SDK for the Deyta platform — memory operations, namespace management, and integrations.

Zero runtime dependencies. Native fetch. Ships ESM and CJS. Recommended runtime: Bun or Node 20+.

Install

npm install @deyta-ai/sdk
# or
bun add @deyta-ai/sdk

Quick start

import { Deyta } from "@deyta-ai/sdk";

const deyta = new Deyta({ apiKey: process.env.DEYTA_API_KEY! });

// Store a memory
await deyta.memory.remember({
  namespace_id: "ns_123",
  content: "The team standup is every Tuesday at 10am UTC.",
  title: "Standup time",
});

// Search memories
const result = await deyta.memory.recall({
  namespace_id: "ns_123",
  query: "when is the standup?",
});

Recommended pattern: namespace sub-client

If you'll issue several operations against the same namespace, scope the client once:

const ns = deyta.namespaces.scope("ns_123");
// or by external reference:
const ns = deyta.namespaces.scopeByExternalRef("user-abc");

await ns.remember({ content: "..." });
await ns.recall({ query: "..." });
await ns.ask({ query: "..." });
await ns.forget({ document_id: "doc_456" });

// Namespace lifecycle
const meta = await ns.metadata();
await ns.delete();

// Integrations scoped to this namespace
const connections = await ns.integrations.list();
const session = await ns.integrations.start({ provider: "google_drive" });

The scope is a lightweight handle — no network call is made when constructing it. Personas expose the same pattern via deyta.personas.scope(...).

Configuration

const deyta = new Deyta({
  apiKey: "your-api-key",     // Required.
  baseUrl: "https://...",      // Optional. Falls back to DEYTA_BASE_URL env var, then https://api.deyta.ai.
  timeout: 30_000,             // Optional. Per-request timeout in ms. Default: 30_000.
  retries: {                   // Optional. Default: 2 retries with exponential backoff.
    maxRetries: 2,
    initialBackoffMs: 500,
    maxBackoffMs: 8_000,
    retryOn: [408, 429, 500, 502, 503, 504],
  },
  fetch: customFetch,          // Optional. Inject a fetch implementation (tests, polyfills).
  logger: (event) => {         // Optional. Receives request/response/retry/error events.
    console.debug("[deyta]", event);
  },
});

Retries auto-apply to idempotent methods (GET, DELETE) on the configured HTTP statuses and on network errors. The Retry-After header is honored when present (both seconds and HTTP-date forms). POST requests are not retried automatically.

Memory

remember

const result = await deyta.memory.remember({
  namespace_id: "ns_123",
  content: "Important information to remember",
  title: "Optional title",
  source: "optional-source",
  metadata: { key: "value" },
  ontology_id: "optional-ontology-id",
});
// result: { document_id, chunks_created, entities_extracted, relationships_created }

recall

const result = await deyta.memory.recall({
  namespace_id: "ns_123",
  query: "what do we know about the project?",
  limit: 10,
  mode: "hybrid",                              // "vector" | "graph" | "hybrid" | "all"
  from: new Date("2026-04-01T00:00:00Z"),      // Optional inclusive lower bound on event time
  until: "2026-04-30T23:59:59Z",               // Optional inclusive upper bound (Date | ISO string)
});
// result: { query, namespace_id, documents, chunks, entities, relationships, usage, engine_info? }
// Pass `verbose: true` to include the optional `engine_info` diagnostic blob.

from and until accept either a Date or an ISO-8601 string. The SDK serializes them to the wire format expected by the API.

forget

const result = await deyta.memory.forget({
  namespace_id: "ns_123",
  document_id: "doc_456",
});
// result: { document_id, deleted }

ask

const result = await deyta.memory.ask({
  namespace_id: "ns_123",
  query: "What are the key project milestones?",
  config: {
    min_recall_limit: 3,
    max_recall_limit: 20,
    total_tokens_limit: 4_000,
    enabled_tools: ["recall"],
  },
  from: new Date("2026-04-01T00:00:00Z"),
  until: new Date("2026-04-30T23:59:59Z"),
});
// result: {
//   answer_id: string;          // upstream run ID — empty string if absent
//   answer: string;             // synthesized answer text
//   sources: AskSource[];       // de-duplicated cited memories
//   usage: AskUsage;            // aggregated token/request counts + per-source breakdown
//   timing: AskTiming;          // started_at / finished_at / duration_ms
// }

The gateway normalizes the upstream agent's verbose streaming event log into this stable non-streaming shape — callers don't have to walk events.

Namespaces

Create

const ns = await deyta.namespaces.create({
  name: "My Namespace",
  description: "A namespace for my project",
  external_reference_id: "my-app-user-123",
});

List + iterate

// One page at a time
const { data, pagination } = await deyta.namespaces.list({ page: 1, page_size: 20 });

// Or walk every page automatically
for await (const ns of deyta.namespaces.iterate({ page_size: 50 })) {
  console.log(ns.id);
}

Get / delete

const ns = await deyta.namespaces.get("ns_123");
const ns = await deyta.namespaces.getByExternalRef("user-abc");
await deyta.namespaces.delete("ns_123");

Integrations

List providers (org-level)

const providers = await deyta.integrations.listProviders();

Connections

Connections target either a namespace or a persona via the typed target discriminator. Provide exactly one of id / external_reference_id. The endpoint returns a paginated { data, pagination } envelope, so iterate explicitly or via the helper:

// One page at a time (namespace-targeted)
const { data, pagination } = await deyta.integrations.listConnections({
  type: "namespace",
  id: "ns_123",
  page: 1,
  page_size: 50,
});

// Persona-targeted
const persona = await deyta.integrations.listConnections({
  type: "persona",
  external_reference_id: "user-abc",
});

// Walk every page automatically
for await (const conn of deyta.integrations.iterateConnections(
  { type: "namespace", id: "ns_123" },
  { page_size: 50 },
)) {
  console.log(conn.id, conn.provider);
}

// Or via the namespace scope — target is captured implicitly
const page = await ns.integrations.list({ page_size: 50 });
for await (const conn of ns.integrations.iterate()) {
  console.log(conn.id);
}

const conn = await deyta.integrations.getConnection("conn_123");
// conn.persona_id — set when the connection's namespace backs a persona; null otherwise

OAuth flow

const start = await deyta.integrations.startConnection({
  target: { type: "persona", id: persona.id },
  provider: "google_drive",
});
// start.session_token — pass to @nangohq/frontend SDK
// start.auth_link_url — OAuth redirect URL

await deyta.integrations.deleteConnection(start.id);

Personas

A persona is a top-level resource that owns a backing namespace created automatically at the same time. The persona's id is stable across SDK calls and is the handle used by every other persona operation.

Create

const persona = await deyta.personas.create({
  subject: "Alice",
  external_reference_id: "user-abc",   // optional
  description: "demo persona",          // optional
});
// persona: { id, org_id, namespace_id, external_reference_id, subject, description, created_at, updated_at }

When the call succeeds, the calling API key is auto-granted access to the persona's backing namespace, so subsequent persona ops work without extra permission steps.

List + iterate

const { data, pagination } = await deyta.personas.list({ page: 1, page_size: 20 });

for await (const p of deyta.personas.iterate({ page_size: 50 })) {
  console.log(p.id, p.subject);
}

Read (with composite document)

const result = await deyta.personas.get(persona.id);
// or: const result = await deyta.personas.getByExternalRef("user-abc");

if (result.built) {
  result.identity;            // identity, traits, episodes, peers, facets, providers, source_event_count, built_at
} else {
  // Local record exists but the composite has not been produced yet — call build() and poll status().
}

Returns 404 NOT_FOUND when the persona doesn't exist locally; 503 when the gateway can't reach the composite service. When the composite simply hasn't been produced yet, the response is shaped { ...persona, built: false } instead of an error.

Update / delete

await deyta.personas.update(persona.id, {
  description: "updated copy",
  external_reference_id: null,    // pass null to clear; omit to leave unchanged
});

await deyta.personas.delete(persona.id);
// Cascades to the backing namespace and all of its connections, labels, and grants.

Build / status

// Defaults: 60 / 14 / 14 / 0.5 (the gateway fills these in when omitted).
const { build_id } = await deyta.personas.build(persona.id);  // HTTP 202

// Override the build window if you need to:
await deyta.personas.build(persona.id, {
  context_window_days: 30,
  focus_past_days: 7,
  focus_future_days: 7,
  focus_ratio: 0.75,
});

const { status, last_built_at } = await deyta.personas.status(persona.id);
// status: "building" | "ready" | "not_built"

build() is not idempotent — the gateway returns 409 CONFLICT if a build is already in flight. Poll status() to follow progress.

Summary (read / regenerate)

// Read the persisted summary. Throws NOT_FOUND if one hasn't been produced yet.
const summary = await deyta.personas.getSummary(persona.id);
// summary: { summary, generated_at, persona_built_at }

// Staleness check: regenerate when the persona has been rebuilt since.
const stale = summary.persona_built_at > summary.generated_at;

// Trigger a fresh generation. Body is optional.
const fresh = await deyta.personas.generateSummary(persona.id, {
  system_prompt: "Write in the third person.",  // optional, ≤ 32 KB
  temperature: 0.4,                              // optional, [0.0, 2.0]
});

generateSummary() returns 409 CONFLICT if a summary generation is already in flight, and 404 NOT_FOUND when the persona itself cannot be found.

Sub-client (scope)

Operate on a single persona without re-stating its identifier on every call:

const p = deyta.personas.scope(persona.id);
// or by external reference:
const p = deyta.personas.scopeByExternalRef("user-abc");

// Persona lifecycle
await p.metadata();
await p.update({ description: "updated" });
await p.build();
await p.status();
await p.getSummary();
await p.generateSummary();
await p.delete();

// Memory — routed through the persona's backing namespace.
// The first call resolves and caches the namespace_id.
await p.remember({ content: "..." });
await p.recall({ query: "..." });

// Integrations targeted at this persona
await p.integrations.list();
await p.integrations.start({ provider: "google_drive" });

Like the namespace scope, this is a lightweight handle — no network call is made when constructing it. When scoped by external reference, the first lifecycle call performs a single metadata() fetch to resolve the persona's id and caches it for subsequent calls.

Error handling

import { DeytaError, DeytaConnectionError } from "@deyta-ai/sdk";

try {
  await deyta.namespaces.get("nonexistent");
} catch (err) {
  if (err instanceof DeytaError) {
    err.code;     // "NOT_FOUND"
    err.status;   // 404
    err.message;  // "Not Found"
  } else if (err instanceof DeytaConnectionError) {
    // Network failure, timeout, or caller-side abort.
    err.cause;    // Original error if available.
  }
}

Error codes: BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, NOT_FOUND, CONFLICT, INTERNAL_ERROR, BAD_GATEWAY, SERVICE_UNAVAILABLE, GATEWAY_TIMEOUT.

Cancellation and per-call options

Every method accepts a second RequestOptions argument:

const controller = new AbortController();
setTimeout(() => controller.abort(), 5_000);

await deyta.memory.recall(
  { namespace_id: "ns_123", query: "search" },
  {
    signal: controller.signal,            // Caller-supplied abort signal
    timeout: 10_000,                      // Per-call timeout override
    headers: { "X-Trace-Id": "abc-123" }, // Extra headers (Authorization is protected)
  },
);

The SDK's timeout and the caller's signal are merged — either can abort the request.

Examples

Runnable examples live under examples/:

  • quickstart.ts — first end-to-end memory roundtrip
  • namespace-scoped.ts — using the sub-client pattern
  • pagination.ts — manual and async-iterator pagination
  • error-handling.ts — typed errors, cancellation, custom retries
DEYTA_API_KEY=… bun run examples/quickstart.ts

Smoke tests

End-to-end scripts under scripts/smoke/ exercise each resource against a real API. Useful for verifying staging or release builds before publishing.

Configuration

Both vars are read from the environment:

| Variable | Required | Purpose | |-------------------|----------|--------------------------------------------------------------| | DEYTA_API_KEY | yes | Bearer token. Scripts exit 1 if missing. | | DEYTA_BASE_URL | no | API base URL. Defaults to https://api.deyta.ai. |

Set them inline, export them in your shell, or drop them into a .env file (bun auto-loads it):

# inline
DEYTA_API_KEY=sk_… DEYTA_BASE_URL=https://staging.deyta.ai bun run smoke

# or shell-exported
export DEYTA_API_KEY=sk_…
export DEYTA_BASE_URL=https://staging.deyta.ai
bun run smoke

Available scripts

| Script | Covers | | ------------------------------ | --------------------------------------------------------------- | | bun run smoke | All four suites in sequence (fail-fast). | | bun run smoke:namespaces | create / get / getByExternalRef / list / iterate / delete | | bun run smoke:memory | scratch namespace → remember / recall / ask / forget → cleanup | | bun run smoke:integrations | listProviders, listConnections (read-only — OAuth skipped) | | bun run smoke:personas | create / get / update / list / status / delete |

smoke:personas accepts -- --build to additionally trigger an async build (not awaited to completion):

bun run smoke:personas -- --build

Every script wraps its work in try / finally so a failure mid-run still cleans up the namespace or persona it created.

Requirements

  • Node.js 20+ (uses AbortSignal.any) or Bun 1.0+
  • TypeScript 5+ for type definitions

License

MIT