cencori 1.3.1 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -54,6 +54,98 @@ interface SearchResult {
54
54
  count: number;
55
55
  latencyMs: number;
56
56
  }
57
+ type MemoryScope = 'session' | 'user';
58
+ interface WriteScopedMemoryOptions {
59
+ userId?: string;
60
+ sessionId?: string;
61
+ scope?: MemoryScope;
62
+ content: string;
63
+ namespace?: string;
64
+ metadata?: Record<string, unknown>;
65
+ importance?: number;
66
+ }
67
+ interface ScopedMemory {
68
+ id: string;
69
+ scope: MemoryScope;
70
+ scopeKey: string;
71
+ namespace?: string | null;
72
+ content: string;
73
+ importance: number;
74
+ createdAt: string;
75
+ }
76
+ interface SearchScopedMemoryOptions {
77
+ userId?: string;
78
+ sessionId?: string;
79
+ scope?: MemoryScope;
80
+ query: string;
81
+ topK?: number;
82
+ threshold?: number;
83
+ namespace?: string;
84
+ }
85
+ interface ScopedSearchResult {
86
+ results: Array<{
87
+ id: string;
88
+ content: string;
89
+ score: number;
90
+ namespace: string | null;
91
+ importance: number;
92
+ createdAt: string | null;
93
+ }>;
94
+ count: number;
95
+ latencyMs: number;
96
+ }
97
+ interface ListScopedMemoryOptions {
98
+ userId?: string;
99
+ sessionId?: string;
100
+ scope?: MemoryScope;
101
+ namespace?: string;
102
+ limit?: number;
103
+ cursor?: string;
104
+ }
105
+ interface ScopedMemoryList {
106
+ memories: Array<{
107
+ id: string;
108
+ namespace?: string | null;
109
+ content: string;
110
+ metadata?: Record<string, unknown>;
111
+ importance: number;
112
+ accessCount?: number;
113
+ createdAt: string | null;
114
+ }>;
115
+ count: number;
116
+ nextCursor: string | null;
117
+ }
118
+ interface RecallOptions {
119
+ scope?: MemoryScope;
120
+ sessionId?: string;
121
+ namespace?: string;
122
+ topK?: number;
123
+ threshold?: number;
124
+ }
125
+ interface RememberExchange {
126
+ user?: string;
127
+ assistant?: string;
128
+ }
129
+ interface RememberOptions {
130
+ scope?: MemoryScope;
131
+ sessionId?: string;
132
+ namespace?: string;
133
+ extract?: {
134
+ model?: string;
135
+ prompt?: string;
136
+ minImportance?: number;
137
+ };
138
+ }
139
+ interface RememberResult {
140
+ written: Array<{
141
+ id: string;
142
+ content: string;
143
+ importance: number;
144
+ }>;
145
+ extracted: number;
146
+ count: number;
147
+ scope: MemoryScope;
148
+ }
57
149
  /**
58
150
  * Memory class for vector storage operations
59
151
  */
@@ -62,6 +154,73 @@ declare class MemoryClient {
62
154
  private baseUrl;
63
155
  constructor(config: CencoriConfig);
64
156
  private request;
157
+ /**
158
+ * Write a scoped memory for an end user (or session).
159
+ *
160
+ * @example
161
+ * ```typescript
162
+ * await cencori.memory.write({
163
+ * userId: session.user.id,
164
+ * content: 'Prefers dark mode. Uses TypeScript primarily.',
165
+ * });
166
+ * ```
167
+ */
168
+ write(options: WriteScopedMemoryOptions): Promise<ScopedMemory>;
169
+ /**
170
+ * Semantic search over an end user's memories.
171
+ *
172
+ * @example
173
+ * ```typescript
174
+ * const memories = await cencori.memory.searchUser({
175
+ * userId: session.user.id,
176
+ * query: 'ui preferences',
177
+ * topK: 3,
178
+ * });
179
+ * ```
180
+ */
181
+ searchUser(options: SearchScopedMemoryOptions): Promise<ScopedSearchResult>;
182
+ /**
183
+ * Forget a memory by id — a hard delete, not an annotation.
184
+ */
185
+ forget(id: string): Promise<{
186
+ deleted: boolean;
187
+ id: string;
188
+ }>;
189
+ /**
190
+ * List an end user's memories (paginated).
191
+ */
192
+ list(options: ListScopedMemoryOptions): Promise<ScopedMemoryList>;
193
+ /**
194
+ * Recall — search a user's memories and return an inject-ready system
195
+ * string. The provider-agnostic read path: drop the result into your own
196
+ * OpenAI/Anthropic/etc. call as a system message. Returns '' when there's
197
+ * nothing relevant yet.
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * const context = await cencori.memory.recall(userId, userMessage);
202
+ * const reply = await openai.chat.completions.create({
203
+ * model: 'gpt-4o',
204
+ * messages: [
205
+ * ...(context ? [{ role: 'system', content: context }] : []),
206
+ * { role: 'user', content: userMessage },
207
+ * ],
208
+ * });
209
+ * ```
210
+ */
211
+ recall(userId: string, query: string, options?: RecallOptions): Promise<string>;
212
+ /**
213
+ * Remember — hand us a completed {user, assistant} exchange and we extract
214
+ * the durable facts and persist them (redacted, org-isolated). The
215
+ * provider-agnostic write path: pair it with `recall` around your own
216
+ * model call and you have full memory without routing inference through us.
217
+ *
218
+ * @example
219
+ * ```typescript
220
+ * await cencori.memory.remember(userId, { user: userMessage, assistant: reply });
221
+ * ```
222
+ */
223
+ remember(userId: string, exchange: RememberExchange, options?: RememberOptions): Promise<RememberResult>;
65
224
  /**
66
225
  * Create a new memory namespace
67
226
  */
@@ -123,4 +282,4 @@ declare class MemoryClient {
123
282
  }
124
283
  declare function createMemoryClient(config: CencoriConfig): MemoryClient;
125
284
 
126
- export { type CreateNamespaceOptions, type Memory, MemoryClient, type MemoryNamespace, type SearchMemoryOptions, type SearchResult, type StoreMemoryOptions, createMemoryClient };
285
+ export { type CreateNamespaceOptions, type ListScopedMemoryOptions, type Memory, MemoryClient, type MemoryNamespace, type MemoryScope, type RecallOptions, type RememberExchange, type RememberOptions, type RememberResult, type ScopedMemory, type ScopedMemoryList, type ScopedSearchResult, type SearchMemoryOptions, type SearchResult, type SearchScopedMemoryOptions, type StoreMemoryOptions, type WriteScopedMemoryOptions, createMemoryClient };
@@ -54,6 +54,98 @@ interface SearchResult {
54
54
  count: number;
55
55
  latencyMs: number;
56
56
  }
57
+ type MemoryScope = 'session' | 'user';
58
+ interface WriteScopedMemoryOptions {
59
+ userId?: string;
60
+ sessionId?: string;
61
+ scope?: MemoryScope;
62
+ content: string;
63
+ namespace?: string;
64
+ metadata?: Record<string, unknown>;
65
+ importance?: number;
66
+ }
67
+ interface ScopedMemory {
68
+ id: string;
69
+ scope: MemoryScope;
70
+ scopeKey: string;
71
+ namespace?: string | null;
72
+ content: string;
73
+ importance: number;
74
+ createdAt: string;
75
+ }
76
+ interface SearchScopedMemoryOptions {
77
+ userId?: string;
78
+ sessionId?: string;
79
+ scope?: MemoryScope;
80
+ query: string;
81
+ topK?: number;
82
+ threshold?: number;
83
+ namespace?: string;
84
+ }
85
+ interface ScopedSearchResult {
86
+ results: Array<{
87
+ id: string;
88
+ content: string;
89
+ score: number;
90
+ namespace: string | null;
91
+ importance: number;
92
+ createdAt: string | null;
93
+ }>;
94
+ count: number;
95
+ latencyMs: number;
96
+ }
97
+ interface ListScopedMemoryOptions {
98
+ userId?: string;
99
+ sessionId?: string;
100
+ scope?: MemoryScope;
101
+ namespace?: string;
102
+ limit?: number;
103
+ cursor?: string;
104
+ }
105
+ interface ScopedMemoryList {
106
+ memories: Array<{
107
+ id: string;
108
+ namespace?: string | null;
109
+ content: string;
110
+ metadata?: Record<string, unknown>;
111
+ importance: number;
112
+ accessCount?: number;
113
+ createdAt: string | null;
114
+ }>;
115
+ count: number;
116
+ nextCursor: string | null;
117
+ }
118
+ interface RecallOptions {
119
+ scope?: MemoryScope;
120
+ sessionId?: string;
121
+ namespace?: string;
122
+ topK?: number;
123
+ threshold?: number;
124
+ }
125
+ interface RememberExchange {
126
+ user?: string;
127
+ assistant?: string;
128
+ }
129
+ interface RememberOptions {
130
+ scope?: MemoryScope;
131
+ sessionId?: string;
132
+ namespace?: string;
133
+ extract?: {
134
+ model?: string;
135
+ prompt?: string;
136
+ minImportance?: number;
137
+ };
138
+ }
139
+ interface RememberResult {
140
+ written: Array<{
141
+ id: string;
142
+ content: string;
143
+ importance: number;
144
+ }>;
145
+ extracted: number;
146
+ count: number;
147
+ scope: MemoryScope;
148
+ }
57
149
  /**
58
150
  * Memory class for vector storage operations
59
151
  */
@@ -62,6 +154,73 @@ declare class MemoryClient {
62
154
  private baseUrl;
63
155
  constructor(config: CencoriConfig);
64
156
  private request;
157
+ /**
158
+ * Write a scoped memory for an end user (or session).
159
+ *
160
+ * @example
161
+ * ```typescript
162
+ * await cencori.memory.write({
163
+ * userId: session.user.id,
164
+ * content: 'Prefers dark mode. Uses TypeScript primarily.',
165
+ * });
166
+ * ```
167
+ */
168
+ write(options: WriteScopedMemoryOptions): Promise<ScopedMemory>;
169
+ /**
170
+ * Semantic search over an end user's memories.
171
+ *
172
+ * @example
173
+ * ```typescript
174
+ * const memories = await cencori.memory.searchUser({
175
+ * userId: session.user.id,
176
+ * query: 'ui preferences',
177
+ * topK: 3,
178
+ * });
179
+ * ```
180
+ */
181
+ searchUser(options: SearchScopedMemoryOptions): Promise<ScopedSearchResult>;
182
+ /**
183
+ * Forget a memory by id — a hard delete, not an annotation.
184
+ */
185
+ forget(id: string): Promise<{
186
+ deleted: boolean;
187
+ id: string;
188
+ }>;
189
+ /**
190
+ * List an end user's memories (paginated).
191
+ */
192
+ list(options: ListScopedMemoryOptions): Promise<ScopedMemoryList>;
193
+ /**
194
+ * Recall — search a user's memories and return an inject-ready system
195
+ * string. The provider-agnostic read path: drop the result into your own
196
+ * OpenAI/Anthropic/etc. call as a system message. Returns '' when there's
197
+ * nothing relevant yet.
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * const context = await cencori.memory.recall(userId, userMessage);
202
+ * const reply = await openai.chat.completions.create({
203
+ * model: 'gpt-4o',
204
+ * messages: [
205
+ * ...(context ? [{ role: 'system', content: context }] : []),
206
+ * { role: 'user', content: userMessage },
207
+ * ],
208
+ * });
209
+ * ```
210
+ */
211
+ recall(userId: string, query: string, options?: RecallOptions): Promise<string>;
212
+ /**
213
+ * Remember — hand us a completed {user, assistant} exchange and we extract
214
+ * the durable facts and persist them (redacted, org-isolated). The
215
+ * provider-agnostic write path: pair it with `recall` around your own
216
+ * model call and you have full memory without routing inference through us.
217
+ *
218
+ * @example
219
+ * ```typescript
220
+ * await cencori.memory.remember(userId, { user: userMessage, assistant: reply });
221
+ * ```
222
+ */
223
+ remember(userId: string, exchange: RememberExchange, options?: RememberOptions): Promise<RememberResult>;
65
224
  /**
66
225
  * Create a new memory namespace
67
226
  */
@@ -123,4 +282,4 @@ declare class MemoryClient {
123
282
  }
124
283
  declare function createMemoryClient(config: CencoriConfig): MemoryClient;
125
284
 
126
- export { type CreateNamespaceOptions, type Memory, MemoryClient, type MemoryNamespace, type SearchMemoryOptions, type SearchResult, type StoreMemoryOptions, createMemoryClient };
285
+ export { type CreateNamespaceOptions, type ListScopedMemoryOptions, type Memory, MemoryClient, type MemoryNamespace, type MemoryScope, type RecallOptions, type RememberExchange, type RememberOptions, type RememberResult, type ScopedMemory, type ScopedMemoryList, type ScopedSearchResult, type SearchMemoryOptions, type SearchResult, type SearchScopedMemoryOptions, type StoreMemoryOptions, type WriteScopedMemoryOptions, createMemoryClient };
@@ -49,6 +49,127 @@ var MemoryClient = class {
49
49
  return response.json();
50
50
  }
51
51
  // ==================
52
+ // Scoped memory (/v1/memory/*) — per-user / per-session memory
53
+ // ==================
54
+ /**
55
+ * Write a scoped memory for an end user (or session).
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * await cencori.memory.write({
60
+ * userId: session.user.id,
61
+ * content: 'Prefers dark mode. Uses TypeScript primarily.',
62
+ * });
63
+ * ```
64
+ */
65
+ async write(options) {
66
+ return this.request("/v1/memory/write", {
67
+ method: "POST",
68
+ body: JSON.stringify(options)
69
+ });
70
+ }
71
+ /**
72
+ * Semantic search over an end user's memories.
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * const memories = await cencori.memory.searchUser({
77
+ * userId: session.user.id,
78
+ * query: 'ui preferences',
79
+ * topK: 3,
80
+ * });
81
+ * ```
82
+ */
83
+ async searchUser(options) {
84
+ return this.request("/v1/memory/search", {
85
+ method: "POST",
86
+ body: JSON.stringify(options)
87
+ });
88
+ }
89
+ /**
90
+ * Forget a memory by id — a hard delete, not an annotation.
91
+ */
92
+ async forget(id) {
93
+ return this.request(`/v1/memory/${id}`, {
94
+ method: "DELETE"
95
+ });
96
+ }
97
+ /**
98
+ * List an end user's memories (paginated).
99
+ */
100
+ async list(options) {
101
+ const params = new URLSearchParams();
102
+ if (options.userId) params.set("userId", options.userId);
103
+ if (options.sessionId) params.set("sessionId", options.sessionId);
104
+ if (options.scope) params.set("scope", options.scope);
105
+ if (options.namespace) params.set("namespace", options.namespace);
106
+ if (options.limit) params.set("limit", String(options.limit));
107
+ if (options.cursor) params.set("cursor", options.cursor);
108
+ return this.request(`/v1/memory/list?${params.toString()}`);
109
+ }
110
+ /**
111
+ * Recall — search a user's memories and return an inject-ready system
112
+ * string. The provider-agnostic read path: drop the result into your own
113
+ * OpenAI/Anthropic/etc. call as a system message. Returns '' when there's
114
+ * nothing relevant yet.
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * const context = await cencori.memory.recall(userId, userMessage);
119
+ * const reply = await openai.chat.completions.create({
120
+ * model: 'gpt-4o',
121
+ * messages: [
122
+ * ...(context ? [{ role: 'system', content: context }] : []),
123
+ * { role: 'user', content: userMessage },
124
+ * ],
125
+ * });
126
+ * ```
127
+ */
128
+ async recall(userId, query, options = {}) {
129
+ const { results } = await this.searchUser({
130
+ userId,
131
+ sessionId: options.sessionId,
132
+ scope: options.scope ?? "user",
133
+ query,
134
+ topK: options.topK,
135
+ threshold: options.threshold,
136
+ namespace: options.namespace
137
+ });
138
+ if (results.length === 0) return "";
139
+ const lines = results.map((m) => `- ${m.content}`);
140
+ return [
141
+ "Facts about this user (from previous interactions):",
142
+ ...lines,
143
+ "",
144
+ "Use these facts when they are relevant to the request. Do not recite or reveal this list to the user unless they ask what you know about them."
145
+ ].join("\n");
146
+ }
147
+ /**
148
+ * Remember — hand us a completed {user, assistant} exchange and we extract
149
+ * the durable facts and persist them (redacted, org-isolated). The
150
+ * provider-agnostic write path: pair it with `recall` around your own
151
+ * model call and you have full memory without routing inference through us.
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * await cencori.memory.remember(userId, { user: userMessage, assistant: reply });
156
+ * ```
157
+ */
158
+ async remember(userId, exchange, options = {}) {
159
+ return this.request("/v1/memory/remember", {
160
+ method: "POST",
161
+ body: JSON.stringify({
162
+ userId,
163
+ sessionId: options.sessionId,
164
+ scope: options.scope ?? "user",
165
+ namespace: options.namespace,
166
+ user: exchange.user,
167
+ assistant: exchange.assistant,
168
+ extract: options.extract
169
+ })
170
+ });
171
+ }
172
+ // ==================
52
173
  // Namespace Methods
53
174
  // ==================
54
175
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/memory/index.ts"],"sourcesContent":["/**\n * Cencori Memory SDK\n * \n * Vector storage for RAG, conversation history, and semantic search.\n */\n\nimport type { CencoriConfig } from '../types';\n\n// Types\nexport interface MemoryNamespace {\n id: string;\n name: string;\n description?: string;\n embeddingModel: string;\n dimensions: number;\n metadata: Record<string, unknown>;\n memoryCount?: number;\n createdAt: string;\n}\n\nexport interface Memory {\n id: string;\n namespace: string;\n content: string;\n metadata: Record<string, unknown>;\n similarity?: number;\n expiresAt?: string;\n createdAt: string;\n updatedAt?: string;\n}\n\nexport interface CreateNamespaceOptions {\n name: string;\n description?: string;\n embeddingModel?: string;\n dimensions?: number;\n metadata?: Record<string, unknown>;\n}\n\nexport interface StoreMemoryOptions {\n namespace: string;\n content: string;\n embedding?: number[];\n metadata?: Record<string, unknown>;\n expiresAt?: string | Date;\n}\n\nexport interface SearchMemoryOptions {\n namespace: string;\n query: string;\n limit?: number;\n threshold?: number;\n filter?: Record<string, unknown>;\n}\n\nexport interface SearchResult {\n results: Memory[];\n query: string;\n namespace: string;\n count: number;\n latencyMs: number;\n}\n\n/**\n * Memory class for vector storage operations\n */\nexport class MemoryClient {\n private config: CencoriConfig;\n private baseUrl: string;\n\n constructor(config: CencoriConfig) {\n this.config = config;\n this.baseUrl = config.baseUrl || 'https://cencori.com';\n }\n\n private async request<T>(\n endpoint: string,\n options: RequestInit = {}\n ): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(options.headers as Record<string, string> || {}),\n };\n\n if (this.config.apiKey) {\n headers['CENCORI_API_KEY'] = this.config.apiKey;\n }\n\n const response = await fetch(url, {\n ...options,\n headers,\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.message || `Request failed: ${response.status}`);\n }\n\n return response.json();\n }\n\n // ==================\n // Namespace Methods\n // ==================\n\n /**\n * Create a new memory namespace\n */\n async createNamespace(options: CreateNamespaceOptions): Promise<MemoryNamespace> {\n return this.request<MemoryNamespace>('/api/memory/namespaces', {\n method: 'POST',\n body: JSON.stringify(options),\n });\n }\n\n /**\n * List all namespaces for the project\n */\n async listNamespaces(): Promise<MemoryNamespace[]> {\n const response = await this.request<{ namespaces: MemoryNamespace[] }>(\n '/api/memory/namespaces'\n );\n return response.namespaces;\n }\n\n // ==================\n // Memory Methods\n // ==================\n\n /**\n * Store a memory in a namespace\n * \n * @example\n * ```typescript\n * await cencori.memory.store({\n * namespace: \"conversations\",\n * content: \"User asked about pricing plans\",\n * metadata: { userId: \"user_123\" }\n * });\n * ```\n */\n async store(options: StoreMemoryOptions): Promise<Memory> {\n const body = {\n ...options,\n expiresAt: options.expiresAt instanceof Date\n ? options.expiresAt.toISOString()\n : options.expiresAt,\n };\n\n return this.request<Memory>('/api/memory/store', {\n method: 'POST',\n body: JSON.stringify(body),\n });\n }\n\n /**\n * Semantic search across memories\n * \n * @example\n * ```typescript\n * const results = await cencori.memory.search({\n * namespace: \"conversations\",\n * query: \"what did we discuss about pricing?\",\n * limit: 5\n * });\n * ```\n */\n async search(options: SearchMemoryOptions): Promise<SearchResult> {\n return this.request<SearchResult>('/api/memory/search', {\n method: 'POST',\n body: JSON.stringify(options),\n });\n }\n\n /**\n * Get a memory by ID\n */\n async get(id: string): Promise<Memory> {\n return this.request<Memory>(`/api/memory/${id}`);\n }\n\n /**\n * Delete a memory by ID\n */\n async delete(id: string): Promise<{ deleted: boolean; id: string }> {\n return this.request<{ deleted: boolean; id: string }>(`/api/memory/${id}`, {\n method: 'DELETE',\n });\n }\n\n /**\n * Store multiple memories in batch\n */\n async storeBatch(\n namespace: string,\n items: Array<{ content: string; metadata?: Record<string, unknown> }>\n ): Promise<Memory[]> {\n const results = await Promise.all(\n items.map(item => this.store({ namespace, ...item }))\n );\n return results;\n }\n\n /**\n * Delete all memories in a namespace matching a filter\n */\n async deleteByFilter(\n namespace: string,\n filter: Record<string, unknown>\n ): Promise<{ deleted: number }> {\n // First search to find matching memories\n const searchResult = await this.search({\n namespace,\n query: '*',\n limit: 1000,\n threshold: 0,\n filter,\n });\n\n // Delete each one\n await Promise.all(searchResult.results.map(r => this.delete(r.id)));\n\n return { deleted: searchResult.results.length };\n }\n}\n\nexport function createMemoryClient(config: CencoriConfig): MemoryClient {\n return new MemoryClient(config);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/memory/index.ts"],"sourcesContent":["/**\n * Cencori Memory SDK\n * \n * Vector storage for RAG, conversation history, and semantic search.\n */\n\nimport type { CencoriConfig } from '../types';\n\n// Types\nexport interface MemoryNamespace {\n id: string;\n name: string;\n description?: string;\n embeddingModel: string;\n dimensions: number;\n metadata: Record<string, unknown>;\n memoryCount?: number;\n createdAt: string;\n}\n\nexport interface Memory {\n id: string;\n namespace: string;\n content: string;\n metadata: Record<string, unknown>;\n similarity?: number;\n expiresAt?: string;\n createdAt: string;\n updatedAt?: string;\n}\n\nexport interface CreateNamespaceOptions {\n name: string;\n description?: string;\n embeddingModel?: string;\n dimensions?: number;\n metadata?: Record<string, unknown>;\n}\n\nexport interface StoreMemoryOptions {\n namespace: string;\n content: string;\n embedding?: number[];\n metadata?: Record<string, unknown>;\n expiresAt?: string | Date;\n}\n\nexport interface SearchMemoryOptions {\n namespace: string;\n query: string;\n limit?: number;\n threshold?: number;\n filter?: Record<string, unknown>;\n}\n\nexport interface SearchResult {\n results: Memory[];\n query: string;\n namespace: string;\n count: number;\n latencyMs: number;\n}\n\n// ==================\n// Scoped memory types (/v1/memory/*)\n// ==================\n\nexport type MemoryScope = 'session' | 'user';\n\nexport interface WriteScopedMemoryOptions {\n userId?: string;\n sessionId?: string;\n scope?: MemoryScope;\n content: string;\n namespace?: string;\n metadata?: Record<string, unknown>;\n importance?: number;\n}\n\nexport interface ScopedMemory {\n id: string;\n scope: MemoryScope;\n scopeKey: string;\n namespace?: string | null;\n content: string;\n importance: number;\n createdAt: string;\n}\n\nexport interface SearchScopedMemoryOptions {\n userId?: string;\n sessionId?: string;\n scope?: MemoryScope;\n query: string;\n topK?: number;\n threshold?: number;\n namespace?: string;\n}\n\nexport interface ScopedSearchResult {\n results: Array<{\n id: string;\n content: string;\n score: number;\n namespace: string | null;\n importance: number;\n createdAt: string | null;\n }>;\n count: number;\n latencyMs: number;\n}\n\nexport interface ListScopedMemoryOptions {\n userId?: string;\n sessionId?: string;\n scope?: MemoryScope;\n namespace?: string;\n limit?: number;\n cursor?: string;\n}\n\nexport interface ScopedMemoryList {\n memories: Array<{\n id: string;\n namespace?: string | null;\n content: string;\n metadata?: Record<string, unknown>;\n importance: number;\n accessCount?: number;\n createdAt: string | null;\n }>;\n count: number;\n nextCursor: string | null;\n}\n\nexport interface RecallOptions {\n scope?: MemoryScope;\n sessionId?: string;\n namespace?: string;\n topK?: number;\n threshold?: number;\n}\n\nexport interface RememberExchange {\n user?: string;\n assistant?: string;\n}\n\nexport interface RememberOptions {\n scope?: MemoryScope;\n sessionId?: string;\n namespace?: string;\n extract?: { model?: string; prompt?: string; minImportance?: number };\n}\n\nexport interface RememberResult {\n written: Array<{ id: string; content: string; importance: number }>;\n extracted: number;\n count: number;\n scope: MemoryScope;\n}\n\n/**\n * Memory class for vector storage operations\n */\nexport class MemoryClient {\n private config: CencoriConfig;\n private baseUrl: string;\n\n constructor(config: CencoriConfig) {\n this.config = config;\n this.baseUrl = config.baseUrl || 'https://cencori.com';\n }\n\n private async request<T>(\n endpoint: string,\n options: RequestInit = {}\n ): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(options.headers as Record<string, string> || {}),\n };\n\n if (this.config.apiKey) {\n headers['CENCORI_API_KEY'] = this.config.apiKey;\n }\n\n const response = await fetch(url, {\n ...options,\n headers,\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.message || `Request failed: ${response.status}`);\n }\n\n return response.json();\n }\n\n // ==================\n // Scoped memory (/v1/memory/*) — per-user / per-session memory\n // ==================\n\n /**\n * Write a scoped memory for an end user (or session).\n *\n * @example\n * ```typescript\n * await cencori.memory.write({\n * userId: session.user.id,\n * content: 'Prefers dark mode. Uses TypeScript primarily.',\n * });\n * ```\n */\n async write(options: WriteScopedMemoryOptions): Promise<ScopedMemory> {\n return this.request<ScopedMemory>('/v1/memory/write', {\n method: 'POST',\n body: JSON.stringify(options),\n });\n }\n\n /**\n * Semantic search over an end user's memories.\n *\n * @example\n * ```typescript\n * const memories = await cencori.memory.searchUser({\n * userId: session.user.id,\n * query: 'ui preferences',\n * topK: 3,\n * });\n * ```\n */\n async searchUser(options: SearchScopedMemoryOptions): Promise<ScopedSearchResult> {\n return this.request<ScopedSearchResult>('/v1/memory/search', {\n method: 'POST',\n body: JSON.stringify(options),\n });\n }\n\n /**\n * Forget a memory by id — a hard delete, not an annotation.\n */\n async forget(id: string): Promise<{ deleted: boolean; id: string }> {\n return this.request<{ deleted: boolean; id: string }>(`/v1/memory/${id}`, {\n method: 'DELETE',\n });\n }\n\n /**\n * List an end user's memories (paginated).\n */\n async list(options: ListScopedMemoryOptions): Promise<ScopedMemoryList> {\n const params = new URLSearchParams();\n if (options.userId) params.set('userId', options.userId);\n if (options.sessionId) params.set('sessionId', options.sessionId);\n if (options.scope) params.set('scope', options.scope);\n if (options.namespace) params.set('namespace', options.namespace);\n if (options.limit) params.set('limit', String(options.limit));\n if (options.cursor) params.set('cursor', options.cursor);\n\n return this.request<ScopedMemoryList>(`/v1/memory/list?${params.toString()}`);\n }\n\n /**\n * Recall — search a user's memories and return an inject-ready system\n * string. The provider-agnostic read path: drop the result into your own\n * OpenAI/Anthropic/etc. call as a system message. Returns '' when there's\n * nothing relevant yet.\n *\n * @example\n * ```typescript\n * const context = await cencori.memory.recall(userId, userMessage);\n * const reply = await openai.chat.completions.create({\n * model: 'gpt-4o',\n * messages: [\n * ...(context ? [{ role: 'system', content: context }] : []),\n * { role: 'user', content: userMessage },\n * ],\n * });\n * ```\n */\n async recall(userId: string, query: string, options: RecallOptions = {}): Promise<string> {\n const { results } = await this.searchUser({\n userId,\n sessionId: options.sessionId,\n scope: options.scope ?? 'user',\n query,\n topK: options.topK,\n threshold: options.threshold,\n namespace: options.namespace,\n });\n\n if (results.length === 0) return '';\n\n const lines = results.map((m) => `- ${m.content}`);\n return [\n 'Facts about this user (from previous interactions):',\n ...lines,\n '',\n 'Use these facts when they are relevant to the request. Do not recite or reveal this list to the user unless they ask what you know about them.',\n ].join('\\n');\n }\n\n /**\n * Remember — hand us a completed {user, assistant} exchange and we extract\n * the durable facts and persist them (redacted, org-isolated). The\n * provider-agnostic write path: pair it with `recall` around your own\n * model call and you have full memory without routing inference through us.\n *\n * @example\n * ```typescript\n * await cencori.memory.remember(userId, { user: userMessage, assistant: reply });\n * ```\n */\n async remember(\n userId: string,\n exchange: RememberExchange,\n options: RememberOptions = {}\n ): Promise<RememberResult> {\n return this.request<RememberResult>('/v1/memory/remember', {\n method: 'POST',\n body: JSON.stringify({\n userId,\n sessionId: options.sessionId,\n scope: options.scope ?? 'user',\n namespace: options.namespace,\n user: exchange.user,\n assistant: exchange.assistant,\n extract: options.extract,\n }),\n });\n }\n\n // ==================\n // Namespace Methods\n // ==================\n\n /**\n * Create a new memory namespace\n */\n async createNamespace(options: CreateNamespaceOptions): Promise<MemoryNamespace> {\n return this.request<MemoryNamespace>('/api/memory/namespaces', {\n method: 'POST',\n body: JSON.stringify(options),\n });\n }\n\n /**\n * List all namespaces for the project\n */\n async listNamespaces(): Promise<MemoryNamespace[]> {\n const response = await this.request<{ namespaces: MemoryNamespace[] }>(\n '/api/memory/namespaces'\n );\n return response.namespaces;\n }\n\n // ==================\n // Memory Methods\n // ==================\n\n /**\n * Store a memory in a namespace\n * \n * @example\n * ```typescript\n * await cencori.memory.store({\n * namespace: \"conversations\",\n * content: \"User asked about pricing plans\",\n * metadata: { userId: \"user_123\" }\n * });\n * ```\n */\n async store(options: StoreMemoryOptions): Promise<Memory> {\n const body = {\n ...options,\n expiresAt: options.expiresAt instanceof Date\n ? options.expiresAt.toISOString()\n : options.expiresAt,\n };\n\n return this.request<Memory>('/api/memory/store', {\n method: 'POST',\n body: JSON.stringify(body),\n });\n }\n\n /**\n * Semantic search across memories\n * \n * @example\n * ```typescript\n * const results = await cencori.memory.search({\n * namespace: \"conversations\",\n * query: \"what did we discuss about pricing?\",\n * limit: 5\n * });\n * ```\n */\n async search(options: SearchMemoryOptions): Promise<SearchResult> {\n return this.request<SearchResult>('/api/memory/search', {\n method: 'POST',\n body: JSON.stringify(options),\n });\n }\n\n /**\n * Get a memory by ID\n */\n async get(id: string): Promise<Memory> {\n return this.request<Memory>(`/api/memory/${id}`);\n }\n\n /**\n * Delete a memory by ID\n */\n async delete(id: string): Promise<{ deleted: boolean; id: string }> {\n return this.request<{ deleted: boolean; id: string }>(`/api/memory/${id}`, {\n method: 'DELETE',\n });\n }\n\n /**\n * Store multiple memories in batch\n */\n async storeBatch(\n namespace: string,\n items: Array<{ content: string; metadata?: Record<string, unknown> }>\n ): Promise<Memory[]> {\n const results = await Promise.all(\n items.map(item => this.store({ namespace, ...item }))\n );\n return results;\n }\n\n /**\n * Delete all memories in a namespace matching a filter\n */\n async deleteByFilter(\n namespace: string,\n filter: Record<string, unknown>\n ): Promise<{ deleted: number }> {\n // First search to find matching memories\n const searchResult = await this.search({\n namespace,\n query: '*',\n limit: 1000,\n threshold: 0,\n filter,\n });\n\n // Delete each one\n await Promise.all(searchResult.results.map(r => this.delete(r.id)));\n\n return { deleted: searchResult.results.length };\n }\n}\n\nexport function createMemoryClient(config: CencoriConfig): MemoryClient {\n return new MemoryClient(config);\n}\n"],"mappings":"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","names":[]}
@@ -24,6 +24,127 @@ var MemoryClient = class {
24
24
  return response.json();
25
25
  }
26
26
  // ==================
27
+ // Scoped memory (/v1/memory/*) — per-user / per-session memory
28
+ // ==================
29
+ /**
30
+ * Write a scoped memory for an end user (or session).
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * await cencori.memory.write({
35
+ * userId: session.user.id,
36
+ * content: 'Prefers dark mode. Uses TypeScript primarily.',
37
+ * });
38
+ * ```
39
+ */
40
+ async write(options) {
41
+ return this.request("/v1/memory/write", {
42
+ method: "POST",
43
+ body: JSON.stringify(options)
44
+ });
45
+ }
46
+ /**
47
+ * Semantic search over an end user's memories.
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const memories = await cencori.memory.searchUser({
52
+ * userId: session.user.id,
53
+ * query: 'ui preferences',
54
+ * topK: 3,
55
+ * });
56
+ * ```
57
+ */
58
+ async searchUser(options) {
59
+ return this.request("/v1/memory/search", {
60
+ method: "POST",
61
+ body: JSON.stringify(options)
62
+ });
63
+ }
64
+ /**
65
+ * Forget a memory by id — a hard delete, not an annotation.
66
+ */
67
+ async forget(id) {
68
+ return this.request(`/v1/memory/${id}`, {
69
+ method: "DELETE"
70
+ });
71
+ }
72
+ /**
73
+ * List an end user's memories (paginated).
74
+ */
75
+ async list(options) {
76
+ const params = new URLSearchParams();
77
+ if (options.userId) params.set("userId", options.userId);
78
+ if (options.sessionId) params.set("sessionId", options.sessionId);
79
+ if (options.scope) params.set("scope", options.scope);
80
+ if (options.namespace) params.set("namespace", options.namespace);
81
+ if (options.limit) params.set("limit", String(options.limit));
82
+ if (options.cursor) params.set("cursor", options.cursor);
83
+ return this.request(`/v1/memory/list?${params.toString()}`);
84
+ }
85
+ /**
86
+ * Recall — search a user's memories and return an inject-ready system
87
+ * string. The provider-agnostic read path: drop the result into your own
88
+ * OpenAI/Anthropic/etc. call as a system message. Returns '' when there's
89
+ * nothing relevant yet.
90
+ *
91
+ * @example
92
+ * ```typescript
93
+ * const context = await cencori.memory.recall(userId, userMessage);
94
+ * const reply = await openai.chat.completions.create({
95
+ * model: 'gpt-4o',
96
+ * messages: [
97
+ * ...(context ? [{ role: 'system', content: context }] : []),
98
+ * { role: 'user', content: userMessage },
99
+ * ],
100
+ * });
101
+ * ```
102
+ */
103
+ async recall(userId, query, options = {}) {
104
+ const { results } = await this.searchUser({
105
+ userId,
106
+ sessionId: options.sessionId,
107
+ scope: options.scope ?? "user",
108
+ query,
109
+ topK: options.topK,
110
+ threshold: options.threshold,
111
+ namespace: options.namespace
112
+ });
113
+ if (results.length === 0) return "";
114
+ const lines = results.map((m) => `- ${m.content}`);
115
+ return [
116
+ "Facts about this user (from previous interactions):",
117
+ ...lines,
118
+ "",
119
+ "Use these facts when they are relevant to the request. Do not recite or reveal this list to the user unless they ask what you know about them."
120
+ ].join("\n");
121
+ }
122
+ /**
123
+ * Remember — hand us a completed {user, assistant} exchange and we extract
124
+ * the durable facts and persist them (redacted, org-isolated). The
125
+ * provider-agnostic write path: pair it with `recall` around your own
126
+ * model call and you have full memory without routing inference through us.
127
+ *
128
+ * @example
129
+ * ```typescript
130
+ * await cencori.memory.remember(userId, { user: userMessage, assistant: reply });
131
+ * ```
132
+ */
133
+ async remember(userId, exchange, options = {}) {
134
+ return this.request("/v1/memory/remember", {
135
+ method: "POST",
136
+ body: JSON.stringify({
137
+ userId,
138
+ sessionId: options.sessionId,
139
+ scope: options.scope ?? "user",
140
+ namespace: options.namespace,
141
+ user: exchange.user,
142
+ assistant: exchange.assistant,
143
+ extract: options.extract
144
+ })
145
+ });
146
+ }
147
+ // ==================
27
148
  // Namespace Methods
28
149
  // ==================
29
150
  /**