TypeScript SDK

HTTP client for the Pensyve REST API. Works in Node.js, Bun, Deno, and edge runtimes.

bun add pensyve
# or
npm install pensyve

The TypeScript SDK is an HTTP client. It requires a running Pensyve REST API server.


Pensyve

new Pensyve(config)

Create a client instance.

ParameterTypeDefaultDescription
configPensyveConfigrequiredClient configuration
import Pensyve from "pensyve";

const p = new Pensyve({ baseUrl: "http://localhost:3000" });
const p = new Pensyve({
  baseUrl: "https://api.pensyve.com",
  namespace: "project-x",
  timeoutMs: 10_000,
  retries: 3,
});

entity(name, kind?)

Create or retrieve an entity.

ParameterTypeDefaultDescription
namestringrequiredEntity name
kindstring"user"One of "agent", "user", "team", "tool"

Returns: Promise<Entity>

const user = await p.entity("alice");
const agent = await p.entity("my-agent", "agent");

recall(query, options?)

Search memories matching a query.

ParameterTypeDefaultDescription
querystringrequiredSearch query
optionsRecallOptions{}Optional filters

Returns: Promise<Memory[]>

const memories = await p.recall("project X deadline");
const memories = await p.recall("deployment steps", {
  entity: "my-agent",
  limit: 10,
  types: ["procedural"],
});

recallGrouped(query, options?)

Recall memories matching a query, clustered by source session.

Runs the same RRF fusion pipeline as recall() and then groups the top-limit memories by episode_id. Memories from the same session cluster into a single SessionGroup sorted in conversation order; semantic and procedural memories appear as singleton groups with sessionId = null.

This is the canonical entry point for "memory as input to an LLM reader" workflows — internal benchmarking on LongMemEval_S confirmed that session-grouped recall produces materially better reader accuracy than flat recall.

ParameterTypeDefaultDescription
querystringrequiredSearch query (must be non-empty)
optionsRecallGroupedOptions{}Limit, ordering, and group cap

Returns: Promise<RecallGroupedResult>

Throws: Error if query is empty (before issuing the HTTP request); PensyveError on API error.

const { groups } = await p.recallGrouped("How many books did I buy?", {
  limit: 50,
  order: "chronological",
});

for (const g of groups) {
  console.log(`### Session ${g.sessionId} (${g.sessionTime}):`);
  for (const m of g.memories) {
    console.log(`  ${m.content}`);
  }
}

Feed the groups directly to a reader prompt — no manual clustering or date-string reordering required.


remember(options)

Store a semantic memory.

ParameterTypeDefaultDescription
optionsRememberOptionsrequiredMemory to store

Returns: Promise<Memory>

const m = await p.remember({
  entity: "alice",
  fact: "Alice prefers dark mode",
  confidence: 0.9,
});

forget(entityName, hardDelete?)

Archive or delete all memories for an entity.

ParameterTypeDefaultDescription
entityNamestringrequiredEntity name
hardDeletebooleanfalsePermanently delete

Returns: Promise<ForgetResult>

const result = await p.forget("alice");
const result = await p.forget("alice", true);

consolidate()

Trigger memory consolidation (promotion, decay, archival).

Returns: Promise<ConsolidateResult>

const stats = await p.consolidate();
// { promoted: 3, decayed: 12, archived: 1 }

stats()

Get runtime statistics.

Returns: Promise<Record<string, unknown>>

const stats = await p.stats();

health()

Check API server health.

Returns: Promise<HealthResult>

const h = await p.health();
// { status: "ok", version: "0.1.0" }

startEpisode(participants)

Begin an episode for recording messages.

ParameterTypeDefaultDescription
participantsstring[]requiredEntity names participating

Returns: Promise<EpisodeHandle>

const ep = await p.startEpisode(["alice", "my-agent"]);
await ep.addMessage("user", "What's the status?");
await ep.addMessage("assistant", "On track for Q2.");
ep.setOutcome("success");
const result = await ep.end();
// { memoriesCreated: 2 }

Types

PensyveConfig

interface PensyveConfig {
  baseUrl: string;
  namespace?: string; // default: "default"
  fetch?: typeof fetch; // custom fetch implementation
  timeoutMs?: number; // default: 30000
  retries?: number; // default: 2
  retryBaseDelayMs?: number; // default: 500
}

Entity

interface Entity {
  id: string;
  name: string;
  kind: string;
}

Memory

interface Memory {
  id: string;
  content: string;
  memoryType: "episodic" | "semantic" | "procedural";
  confidence: number;
  stability: number;
  score?: number;
  /**
   * When the described event occurred (ISO 8601 / RFC 3339). Only set
   * for episodic memories that were ingested with an explicit `when=`.
   */
  eventTime?: string;
}

RecallOptions

interface RecallOptions {
  entity?: string;
  limit?: number; // default: 5
  types?: Array<"episodic" | "semantic" | "procedural">;
}

SessionGroup

A cluster of memories sharing a source conversation session. Returned by recallGrouped().

interface SessionGroup {
  /**
   * Episode UUID, or `null` for semantic / procedural memories with no
   * episode ancestor.
   */
  sessionId: string | null;
  /**
   * Earliest event time across the group's memories
   * (ISO 8601 / RFC 3339).
   */
  sessionTime: string;
  /** Memories in conversation order (event time ascending). */
  memories: Memory[];
  /** Aggregated relevance — max RRF score across the group's members. */
  groupScore: number;
}

RecallGroupedOptions

type RecallGroupedOrder = "chronological" | "relevance";

interface RecallGroupedOptions {
  /** Max memories to consider across all groups (default: 50). */
  limit?: number;
  /**
   * `"chronological"` (default, oldest session first) or `"relevance"`
   * (highest-scoring session first).
   */
  order?: RecallGroupedOrder;
  /** Optional cap on the number of groups returned. */
  maxGroups?: number;
}

RecallGroupedResult

interface RecallGroupedResult {
  groups: SessionGroup[];
}

RememberOptions

interface RememberOptions {
  entity: string;
  fact: string;
  confidence?: number; // default: 0.8
}

ForgetResult

interface ForgetResult {
  forgottenCount: number;
}

ConsolidateResult

interface ConsolidateResult {
  promoted: number;
  decayed: number;
  archived: number;
}

HealthResult

interface HealthResult {
  status: string;
  version: string;
}

EpisodeHandle

interface EpisodeHandle {
  addMessage(role: string, content: string): Promise<void>;
  setOutcome(outcome: "success" | "failure" | "partial"): void;
  end(): Promise<{ memoriesCreated: number }>;
}

PensyveError

Thrown on non-2xx responses. 4xx errors are not retried. 5xx errors are retried per the retries config.

PropertyTypeDescription
statusnumberHTTP status code
statusTextstringHTTP status text
detailstring | nullError detail from API response body
endpointstringDescription of the operation that failed
import { PensyveError } from "pensyve";

try {
  await p.recall("test");
} catch (err) {
  if (err instanceof PensyveError && err.status === 401) {
    console.error("Invalid API key");
  }
}