@usewhisper/sdk 2.0.0 → 2.2.0

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.
package/index.mjs CHANGED
@@ -13,7 +13,6 @@ var Whisper = class {
13
13
  baseUrl: options.baseUrl,
14
14
  project: options.project || "default"
15
15
  };
16
- if (options.orgId) clientConfig.orgId = options.orgId;
17
16
  if (options.timeoutMs) clientConfig.timeoutMs = options.timeoutMs;
18
17
  if (options.retry) clientConfig.retry = options.retry;
19
18
  this.client = new WhisperContext(clientConfig);
@@ -22,12 +21,14 @@ var Whisper = class {
22
21
  apiKey: options.apiKey,
23
22
  baseUrl: options.baseUrl || "https://context.usewhisper.dev",
24
23
  project: options.project || "default",
25
- orgId: options.orgId || "",
26
24
  timeoutMs: options.timeoutMs || 15e3,
27
25
  retry: finalRetry,
28
26
  contextLimit: options.contextLimit ?? 10,
29
27
  memoryTypes: options.memoryTypes ?? ["factual", "preference", "event", "goal", "relationship", "opinion", "instruction"],
30
- contextPrefix: options.contextPrefix ?? "Relevant context:"
28
+ contextPrefix: options.contextPrefix ?? "Relevant context:",
29
+ autoExtract: options.autoExtract ?? true,
30
+ autoExtractMinConfidence: options.autoExtractMinConfidence ?? 0.65,
31
+ maxMemoriesPerCapture: options.maxMemoriesPerCapture ?? 5
31
32
  };
32
33
  }
33
34
  /**
@@ -102,6 +103,47 @@ ${context}` : "",
102
103
  return { success: false };
103
104
  }
104
105
  try {
106
+ if (this.options.autoExtract) {
107
+ const extraction = await this.client.extractMemories({
108
+ project: options?.project ?? this.options.project,
109
+ message: content,
110
+ user_id: options?.userId ?? this.userId,
111
+ session_id: options?.sessionId ?? this.sessionId,
112
+ enable_pattern: true,
113
+ enable_inference: true,
114
+ min_confidence: this.options.autoExtractMinConfidence
115
+ });
116
+ const extractedMemories = (extraction.all || []).filter((m) => (m.confidence || 0) >= this.options.autoExtractMinConfidence).slice(0, this.options.maxMemoriesPerCapture);
117
+ if (extractedMemories.length > 0) {
118
+ const bulk = await this.client.addMemoriesBulk({
119
+ project: options?.project ?? this.options.project,
120
+ async: false,
121
+ memories: extractedMemories.map((m) => ({
122
+ content: m.content,
123
+ memory_type: m.memoryType,
124
+ user_id: options?.userId ?? this.userId,
125
+ session_id: options?.sessionId ?? this.sessionId,
126
+ importance: Math.max(0.5, Math.min(1, m.confidence || 0.7)),
127
+ confidence: m.confidence || 0.7,
128
+ entity_mentions: m.entityMentions || [],
129
+ event_date: m.eventDate || void 0,
130
+ metadata: {
131
+ extracted: true,
132
+ extraction_method: extraction.extractionMethod,
133
+ extraction_reasoning: m.reasoning,
134
+ inferred: Boolean(m.inferred)
135
+ }
136
+ }))
137
+ });
138
+ const memoryIds = this.extractMemoryIdsFromBulkResponse(bulk);
139
+ return {
140
+ success: true,
141
+ memoryId: memoryIds[0],
142
+ memoryIds: memoryIds.length > 0 ? memoryIds : void 0,
143
+ extracted: extractedMemories.length
144
+ };
145
+ }
146
+ }
105
147
  const result = await this.client.addMemory({
106
148
  project: options?.project ?? this.options.project,
107
149
  content,
@@ -147,15 +189,156 @@ ${context}` : "",
147
189
  return { success: false, extracted: 0 };
148
190
  }
149
191
  }
192
+ /**
193
+ * Run a full agent turn with automatic memory read (before) + write (after).
194
+ */
195
+ async runTurn(params) {
196
+ const contextResult = await this.getContext(params.userMessage, {
197
+ userId: params.userId,
198
+ sessionId: params.sessionId,
199
+ project: params.project,
200
+ limit: params.limit
201
+ });
202
+ const prompt = contextResult.context ? `${contextResult.context}
203
+
204
+ User: ${params.userMessage}` : params.userMessage;
205
+ const response = await params.generate(prompt);
206
+ const captureResult = await this.captureSession(
207
+ [
208
+ { role: "user", content: params.userMessage },
209
+ { role: "assistant", content: response }
210
+ ],
211
+ {
212
+ userId: params.userId,
213
+ sessionId: params.sessionId,
214
+ project: params.project
215
+ }
216
+ );
217
+ return {
218
+ response,
219
+ context: contextResult.context,
220
+ count: contextResult.count,
221
+ extracted: captureResult.extracted
222
+ };
223
+ }
150
224
  /**
151
225
  * Direct access to WhisperContext for advanced usage
152
226
  */
153
227
  raw() {
154
228
  return this.client;
155
229
  }
230
+ extractMemoryIdsFromBulkResponse(bulkResponse) {
231
+ const ids = [];
232
+ if (Array.isArray(bulkResponse?.memories)) {
233
+ for (const memory of bulkResponse.memories) {
234
+ if (memory?.id) ids.push(memory.id);
235
+ }
236
+ }
237
+ if (bulkResponse?.memory?.id) {
238
+ ids.push(bulkResponse.memory.id);
239
+ }
240
+ if (bulkResponse?.id) {
241
+ ids.push(bulkResponse.id);
242
+ }
243
+ return Array.from(new Set(ids));
244
+ }
156
245
  };
157
246
  var whisper_agent_default = Whisper;
158
247
 
248
+ // ../src/sdk/middleware.ts
249
+ var WhisperAgentMiddleware = class {
250
+ whisper;
251
+ promptBuilder;
252
+ constructor(config) {
253
+ this.whisper = new Whisper(config);
254
+ this.promptBuilder = config.promptBuilder || (({ context, userMessage }) => {
255
+ if (!context) return userMessage;
256
+ return `${context}
257
+
258
+ User: ${userMessage}`;
259
+ });
260
+ }
261
+ async beforeTurn(params) {
262
+ const contextResult = await this.whisper.getContext(params.userMessage, {
263
+ userId: params.userId,
264
+ sessionId: params.sessionId,
265
+ project: params.project,
266
+ limit: params.contextLimit
267
+ });
268
+ const prompt = this.promptBuilder({
269
+ context: contextResult.context,
270
+ userMessage: params.userMessage
271
+ });
272
+ return {
273
+ prompt,
274
+ context: contextResult.context,
275
+ contextCount: contextResult.count
276
+ };
277
+ }
278
+ async afterTurn(params) {
279
+ return this.whisper.captureSession(
280
+ [
281
+ { role: "user", content: params.userMessage },
282
+ { role: "assistant", content: params.assistantMessage }
283
+ ],
284
+ {
285
+ userId: params.userId,
286
+ sessionId: params.sessionId,
287
+ project: params.project
288
+ }
289
+ );
290
+ }
291
+ async wrapGenerate(params) {
292
+ const before = await this.beforeTurn(params);
293
+ const response = await params.generate(before.prompt);
294
+ const after = await this.afterTurn({
295
+ userMessage: params.userMessage,
296
+ assistantMessage: response,
297
+ userId: params.userId,
298
+ sessionId: params.sessionId,
299
+ project: params.project
300
+ });
301
+ return {
302
+ response,
303
+ prompt: before.prompt,
304
+ context: before.context,
305
+ contextCount: before.contextCount,
306
+ extracted: after.extracted
307
+ };
308
+ }
309
+ raw() {
310
+ return this.whisper;
311
+ }
312
+ };
313
+ function createAgentMiddleware(config) {
314
+ return new WhisperAgentMiddleware(config);
315
+ }
316
+
317
+ // ../src/sdk/graph-utils.ts
318
+ function sanitizeId(id) {
319
+ return `n_${id.replace(/[^a-zA-Z0-9_]/g, "_")}`;
320
+ }
321
+ function shortLabel(input, max = 48) {
322
+ const text = (input || "").replace(/\s+/g, " ").trim();
323
+ if (text.length <= max) return text;
324
+ return `${text.slice(0, max - 3)}...`;
325
+ }
326
+ function memoryGraphToMermaid(graph) {
327
+ const lines = ["flowchart LR"];
328
+ for (const node of graph.nodes || []) {
329
+ const sid = sanitizeId(node.id);
330
+ const label = shortLabel(node.label || node.id);
331
+ lines.push(` ${sid}["${label.replace(/"/g, '\\"')}"]`);
332
+ }
333
+ for (const edge of graph.edges || []) {
334
+ const s = sanitizeId(edge.source);
335
+ const t = sanitizeId(edge.target);
336
+ const rel = shortLabel(edge.type || "rel", 18).replace(/"/g, '\\"');
337
+ lines.push(` ${s} -->|${rel}| ${t}`);
338
+ }
339
+ return lines.join("\n");
340
+ }
341
+
159
342
  // ../src/sdk/index.ts
160
343
  var WhisperError = class extends Error {
161
344
  code;
@@ -186,11 +369,24 @@ function getBackoffDelay(attempt, base, max) {
186
369
  function isLikelyProjectId(projectRef) {
187
370
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(projectRef);
188
371
  }
372
+ function normalizeBaseUrl(url) {
373
+ let normalized = url.trim().replace(/\/+$/, "");
374
+ normalized = normalized.replace(/\/api\/v1$/i, "");
375
+ normalized = normalized.replace(/\/v1$/i, "");
376
+ normalized = normalized.replace(/\/api$/i, "");
377
+ return normalized;
378
+ }
379
+ function normalizeEndpoint(endpoint) {
380
+ const withLeadingSlash = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
381
+ if (/^\/api\/v1(\/|$)/i.test(withLeadingSlash)) {
382
+ return withLeadingSlash.replace(/^\/api/i, "");
383
+ }
384
+ return withLeadingSlash;
385
+ }
189
386
  var WhisperContext = class _WhisperContext {
190
387
  apiKey;
191
388
  baseUrl;
192
389
  defaultProject;
193
- orgId;
194
390
  timeoutMs;
195
391
  retryConfig;
196
392
  projectRefToId = /* @__PURE__ */ new Map();
@@ -204,9 +400,8 @@ var WhisperContext = class _WhisperContext {
204
400
  });
205
401
  }
206
402
  this.apiKey = config.apiKey;
207
- this.baseUrl = config.baseUrl || "https://context.usewhisper.dev";
403
+ this.baseUrl = normalizeBaseUrl(config.baseUrl || "https://context.usewhisper.dev");
208
404
  this.defaultProject = config.project;
209
- this.orgId = config.orgId;
210
405
  this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
211
406
  this.retryConfig = {
212
407
  maxAttempts: config.retry?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS,
@@ -219,7 +414,6 @@ var WhisperContext = class _WhisperContext {
219
414
  apiKey: this.apiKey,
220
415
  baseUrl: this.baseUrl,
221
416
  project,
222
- orgId: this.orgId,
223
417
  timeoutMs: this.timeoutMs,
224
418
  retry: this.retryConfig
225
419
  });
@@ -336,20 +530,28 @@ var WhisperContext = class _WhisperContext {
336
530
  }
337
531
  async request(endpoint, options = {}) {
338
532
  const maxAttempts = Math.max(1, this.retryConfig.maxAttempts);
533
+ const normalizedEndpoint = normalizeEndpoint(endpoint);
339
534
  let lastError;
340
535
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
341
536
  const controller = new AbortController();
342
537
  const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
343
538
  try {
344
- const response = await fetch(`${this.baseUrl}${endpoint}`, {
539
+ const headers = {
540
+ "Content-Type": "application/json",
541
+ ...options.headers
542
+ };
543
+ const hasAuthHeader = Object.keys(headers).some((k) => k.toLowerCase() === "authorization");
544
+ const hasApiKeyHeader = Object.keys(headers).some((k) => k.toLowerCase() === "x-api-key");
545
+ if (!hasAuthHeader) {
546
+ headers.Authorization = `Bearer ${this.apiKey}`;
547
+ }
548
+ if (!hasApiKeyHeader) {
549
+ headers["X-API-Key"] = this.apiKey;
550
+ }
551
+ const response = await fetch(`${this.baseUrl}${normalizedEndpoint}`, {
345
552
  ...options,
346
553
  signal: controller.signal,
347
- headers: {
348
- Authorization: `Bearer ${this.apiKey}`,
349
- "Content-Type": "application/json",
350
- ...this.orgId ? { "X-Whisper-Org-Id": this.orgId } : {},
351
- ...options.headers
352
- }
554
+ headers
353
555
  });
354
556
  clearTimeout(timeout);
355
557
  if (!response.ok) {
@@ -538,6 +740,34 @@ var WhisperContext = class _WhisperContext {
538
740
  return { id, success: true, path: "legacy", fallback_used: true };
539
741
  });
540
742
  }
743
+ async addMemoriesBulk(params) {
744
+ const projectRef = this.getRequiredProject(params.project);
745
+ return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/bulk", {
746
+ method: "POST",
747
+ body: JSON.stringify({ ...params, project })
748
+ }));
749
+ }
750
+ async extractMemories(params) {
751
+ const projectRef = this.getRequiredProject(params.project);
752
+ return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/extract", {
753
+ method: "POST",
754
+ body: JSON.stringify({ ...params, project })
755
+ }));
756
+ }
757
+ async extractSessionMemories(params) {
758
+ const projectRef = this.getRequiredProject(params.project);
759
+ return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/extract/session", {
760
+ method: "POST",
761
+ body: JSON.stringify({
762
+ ...params,
763
+ project,
764
+ messages: params.messages.map((m) => ({
765
+ ...m,
766
+ timestamp: m.timestamp || (/* @__PURE__ */ new Date()).toISOString()
767
+ }))
768
+ })
769
+ }));
770
+ }
541
771
  async searchMemories(params) {
542
772
  const projectRef = this.getRequiredProject(params.project);
543
773
  return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/search", {
@@ -610,6 +840,26 @@ var WhisperContext = class _WhisperContext {
610
840
  async getMemoryRelations(memoryId) {
611
841
  return this.request(`/v1/memory/${memoryId}/relations`);
612
842
  }
843
+ async getMemoryGraph(params) {
844
+ const project = await this.resolveProjectId(this.getRequiredProject(params.project));
845
+ const query = new URLSearchParams({
846
+ project,
847
+ ...params.user_id && { user_id: params.user_id },
848
+ ...params.session_id && { session_id: params.session_id },
849
+ ...params.include_inactive !== void 0 && { include_inactive: String(params.include_inactive) },
850
+ ...params.limit !== void 0 && { limit: String(params.limit) }
851
+ });
852
+ return this.request(`/v1/memory/graph?${query}`);
853
+ }
854
+ async getConversationGraph(params) {
855
+ const project = await this.resolveProjectId(this.getRequiredProject(params.project));
856
+ const query = new URLSearchParams({
857
+ project,
858
+ ...params.include_inactive !== void 0 && { include_inactive: String(params.include_inactive) },
859
+ ...params.limit !== void 0 && { limit: String(params.limit) }
860
+ });
861
+ return this.request(`/v1/memory/graph/conversation/${params.session_id}?${query}`);
862
+ }
613
863
  async oracleSearch(params) {
614
864
  const project = await this.resolveProjectId(this.getRequiredProject(params.project));
615
865
  return this.request("/v1/oracle/search", {
@@ -734,6 +984,9 @@ var WhisperContext = class _WhisperContext {
734
984
  };
735
985
  memory = {
736
986
  add: (params) => this.addMemory(params),
987
+ addBulk: (params) => this.addMemoriesBulk(params),
988
+ extract: (params) => this.extractMemories(params),
989
+ extractSession: (params) => this.extractSessionMemories(params),
737
990
  search: (params) => this.searchMemories(params),
738
991
  searchSOTA: (params) => this.searchMemoriesSOTA(params),
739
992
  ingestSession: (params) => this.ingestSession(params),
@@ -743,6 +996,8 @@ var WhisperContext = class _WhisperContext {
743
996
  update: (memoryId, params) => this.updateMemory(memoryId, params),
744
997
  delete: (memoryId) => this.deleteMemory(memoryId),
745
998
  getRelations: (memoryId) => this.getMemoryRelations(memoryId),
999
+ getGraph: (params) => this.getMemoryGraph(params),
1000
+ getConversationGraph: (params) => this.getConversationGraph(params),
746
1001
  consolidate: (params) => this.consolidateMemories(params),
747
1002
  updateDecay: (params) => this.updateImportanceDecay(params),
748
1003
  getImportanceStats: (project) => this.getImportanceStats(project)
@@ -772,8 +1027,11 @@ var WhisperContext = class _WhisperContext {
772
1027
  var index_default = WhisperContext;
773
1028
  export {
774
1029
  Whisper,
1030
+ WhisperAgentMiddleware,
775
1031
  WhisperContext,
776
1032
  whisper_agent_default as WhisperDefault,
777
1033
  WhisperError,
778
- index_default as default
1034
+ createAgentMiddleware,
1035
+ index_default as default,
1036
+ memoryGraphToMermaid
779
1037
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usewhisper/sdk",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "scripts": {
5
5
  "build": "tsup ../src/sdk/index.ts --format esm,cjs --dts --out-dir .",
6
6
  "prepublishOnly": "npm run build"