TypeScript SDK
HTTP client for the Pensyve REST API. Works in Node.js, Bun, Deno, and edge runtimes.
bun add pensyve
# or
npm install pensyveThe TypeScript SDK is an HTTP client. It requires a running Pensyve REST API server.
Pensyve
new Pensyve(config)
Create a client instance.
| Parameter | Type | Default | Description |
|---|---|---|---|
config | PensyveConfig | required | Client 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.
| Parameter | Type | Default | Description |
|---|---|---|---|
name | string | required | Entity name |
kind | string | "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.
| Parameter | Type | Default | Description |
|---|---|---|---|
query | string | required | Search query |
options | RecallOptions | {} | 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.
| Parameter | Type | Default | Description |
|---|---|---|---|
query | string | required | Search query (must be non-empty) |
options | RecallGroupedOptions | {} | 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.
| Parameter | Type | Default | Description |
|---|---|---|---|
options | RememberOptions | required | Memory 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.
| Parameter | Type | Default | Description |
|---|---|---|---|
entityName | string | required | Entity name |
hardDelete | boolean | false | Permanently 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.
| Parameter | Type | Default | Description |
|---|---|---|---|
participants | string[] | required | Entity 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.
| Property | Type | Description |
|---|---|---|
status | number | HTTP status code |
statusText | string | HTTP status text |
detail | string | null | Error detail from API response body |
endpoint | string | Description 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");
}
}