@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.js CHANGED
@@ -21,10 +21,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  Whisper: () => Whisper,
24
+ WhisperAgentMiddleware: () => WhisperAgentMiddleware,
24
25
  WhisperContext: () => WhisperContext,
25
26
  WhisperDefault: () => whisper_agent_default,
26
27
  WhisperError: () => WhisperError,
27
- default: () => index_default
28
+ createAgentMiddleware: () => createAgentMiddleware,
29
+ default: () => index_default,
30
+ memoryGraphToMermaid: () => memoryGraphToMermaid
28
31
  });
29
32
  module.exports = __toCommonJS(index_exports);
30
33
 
@@ -43,7 +46,6 @@ var Whisper = class {
43
46
  baseUrl: options.baseUrl,
44
47
  project: options.project || "default"
45
48
  };
46
- if (options.orgId) clientConfig.orgId = options.orgId;
47
49
  if (options.timeoutMs) clientConfig.timeoutMs = options.timeoutMs;
48
50
  if (options.retry) clientConfig.retry = options.retry;
49
51
  this.client = new WhisperContext(clientConfig);
@@ -52,12 +54,14 @@ var Whisper = class {
52
54
  apiKey: options.apiKey,
53
55
  baseUrl: options.baseUrl || "https://context.usewhisper.dev",
54
56
  project: options.project || "default",
55
- orgId: options.orgId || "",
56
57
  timeoutMs: options.timeoutMs || 15e3,
57
58
  retry: finalRetry,
58
59
  contextLimit: options.contextLimit ?? 10,
59
60
  memoryTypes: options.memoryTypes ?? ["factual", "preference", "event", "goal", "relationship", "opinion", "instruction"],
60
- contextPrefix: options.contextPrefix ?? "Relevant context:"
61
+ contextPrefix: options.contextPrefix ?? "Relevant context:",
62
+ autoExtract: options.autoExtract ?? true,
63
+ autoExtractMinConfidence: options.autoExtractMinConfidence ?? 0.65,
64
+ maxMemoriesPerCapture: options.maxMemoriesPerCapture ?? 5
61
65
  };
62
66
  }
63
67
  /**
@@ -132,6 +136,47 @@ ${context}` : "",
132
136
  return { success: false };
133
137
  }
134
138
  try {
139
+ if (this.options.autoExtract) {
140
+ const extraction = await this.client.extractMemories({
141
+ project: options?.project ?? this.options.project,
142
+ message: content,
143
+ user_id: options?.userId ?? this.userId,
144
+ session_id: options?.sessionId ?? this.sessionId,
145
+ enable_pattern: true,
146
+ enable_inference: true,
147
+ min_confidence: this.options.autoExtractMinConfidence
148
+ });
149
+ const extractedMemories = (extraction.all || []).filter((m) => (m.confidence || 0) >= this.options.autoExtractMinConfidence).slice(0, this.options.maxMemoriesPerCapture);
150
+ if (extractedMemories.length > 0) {
151
+ const bulk = await this.client.addMemoriesBulk({
152
+ project: options?.project ?? this.options.project,
153
+ async: false,
154
+ memories: extractedMemories.map((m) => ({
155
+ content: m.content,
156
+ memory_type: m.memoryType,
157
+ user_id: options?.userId ?? this.userId,
158
+ session_id: options?.sessionId ?? this.sessionId,
159
+ importance: Math.max(0.5, Math.min(1, m.confidence || 0.7)),
160
+ confidence: m.confidence || 0.7,
161
+ entity_mentions: m.entityMentions || [],
162
+ event_date: m.eventDate || void 0,
163
+ metadata: {
164
+ extracted: true,
165
+ extraction_method: extraction.extractionMethod,
166
+ extraction_reasoning: m.reasoning,
167
+ inferred: Boolean(m.inferred)
168
+ }
169
+ }))
170
+ });
171
+ const memoryIds = this.extractMemoryIdsFromBulkResponse(bulk);
172
+ return {
173
+ success: true,
174
+ memoryId: memoryIds[0],
175
+ memoryIds: memoryIds.length > 0 ? memoryIds : void 0,
176
+ extracted: extractedMemories.length
177
+ };
178
+ }
179
+ }
135
180
  const result = await this.client.addMemory({
136
181
  project: options?.project ?? this.options.project,
137
182
  content,
@@ -177,15 +222,156 @@ ${context}` : "",
177
222
  return { success: false, extracted: 0 };
178
223
  }
179
224
  }
225
+ /**
226
+ * Run a full agent turn with automatic memory read (before) + write (after).
227
+ */
228
+ async runTurn(params) {
229
+ const contextResult = await this.getContext(params.userMessage, {
230
+ userId: params.userId,
231
+ sessionId: params.sessionId,
232
+ project: params.project,
233
+ limit: params.limit
234
+ });
235
+ const prompt = contextResult.context ? `${contextResult.context}
236
+
237
+ User: ${params.userMessage}` : params.userMessage;
238
+ const response = await params.generate(prompt);
239
+ const captureResult = await this.captureSession(
240
+ [
241
+ { role: "user", content: params.userMessage },
242
+ { role: "assistant", content: response }
243
+ ],
244
+ {
245
+ userId: params.userId,
246
+ sessionId: params.sessionId,
247
+ project: params.project
248
+ }
249
+ );
250
+ return {
251
+ response,
252
+ context: contextResult.context,
253
+ count: contextResult.count,
254
+ extracted: captureResult.extracted
255
+ };
256
+ }
180
257
  /**
181
258
  * Direct access to WhisperContext for advanced usage
182
259
  */
183
260
  raw() {
184
261
  return this.client;
185
262
  }
263
+ extractMemoryIdsFromBulkResponse(bulkResponse) {
264
+ const ids = [];
265
+ if (Array.isArray(bulkResponse?.memories)) {
266
+ for (const memory of bulkResponse.memories) {
267
+ if (memory?.id) ids.push(memory.id);
268
+ }
269
+ }
270
+ if (bulkResponse?.memory?.id) {
271
+ ids.push(bulkResponse.memory.id);
272
+ }
273
+ if (bulkResponse?.id) {
274
+ ids.push(bulkResponse.id);
275
+ }
276
+ return Array.from(new Set(ids));
277
+ }
186
278
  };
187
279
  var whisper_agent_default = Whisper;
188
280
 
281
+ // ../src/sdk/middleware.ts
282
+ var WhisperAgentMiddleware = class {
283
+ whisper;
284
+ promptBuilder;
285
+ constructor(config) {
286
+ this.whisper = new Whisper(config);
287
+ this.promptBuilder = config.promptBuilder || (({ context, userMessage }) => {
288
+ if (!context) return userMessage;
289
+ return `${context}
290
+
291
+ User: ${userMessage}`;
292
+ });
293
+ }
294
+ async beforeTurn(params) {
295
+ const contextResult = await this.whisper.getContext(params.userMessage, {
296
+ userId: params.userId,
297
+ sessionId: params.sessionId,
298
+ project: params.project,
299
+ limit: params.contextLimit
300
+ });
301
+ const prompt = this.promptBuilder({
302
+ context: contextResult.context,
303
+ userMessage: params.userMessage
304
+ });
305
+ return {
306
+ prompt,
307
+ context: contextResult.context,
308
+ contextCount: contextResult.count
309
+ };
310
+ }
311
+ async afterTurn(params) {
312
+ return this.whisper.captureSession(
313
+ [
314
+ { role: "user", content: params.userMessage },
315
+ { role: "assistant", content: params.assistantMessage }
316
+ ],
317
+ {
318
+ userId: params.userId,
319
+ sessionId: params.sessionId,
320
+ project: params.project
321
+ }
322
+ );
323
+ }
324
+ async wrapGenerate(params) {
325
+ const before = await this.beforeTurn(params);
326
+ const response = await params.generate(before.prompt);
327
+ const after = await this.afterTurn({
328
+ userMessage: params.userMessage,
329
+ assistantMessage: response,
330
+ userId: params.userId,
331
+ sessionId: params.sessionId,
332
+ project: params.project
333
+ });
334
+ return {
335
+ response,
336
+ prompt: before.prompt,
337
+ context: before.context,
338
+ contextCount: before.contextCount,
339
+ extracted: after.extracted
340
+ };
341
+ }
342
+ raw() {
343
+ return this.whisper;
344
+ }
345
+ };
346
+ function createAgentMiddleware(config) {
347
+ return new WhisperAgentMiddleware(config);
348
+ }
349
+
350
+ // ../src/sdk/graph-utils.ts
351
+ function sanitizeId(id) {
352
+ return `n_${id.replace(/[^a-zA-Z0-9_]/g, "_")}`;
353
+ }
354
+ function shortLabel(input, max = 48) {
355
+ const text = (input || "").replace(/\s+/g, " ").trim();
356
+ if (text.length <= max) return text;
357
+ return `${text.slice(0, max - 3)}...`;
358
+ }
359
+ function memoryGraphToMermaid(graph) {
360
+ const lines = ["flowchart LR"];
361
+ for (const node of graph.nodes || []) {
362
+ const sid = sanitizeId(node.id);
363
+ const label = shortLabel(node.label || node.id);
364
+ lines.push(` ${sid}["${label.replace(/"/g, '\\"')}"]`);
365
+ }
366
+ for (const edge of graph.edges || []) {
367
+ const s = sanitizeId(edge.source);
368
+ const t = sanitizeId(edge.target);
369
+ const rel = shortLabel(edge.type || "rel", 18).replace(/"/g, '\\"');
370
+ lines.push(` ${s} -->|${rel}| ${t}`);
371
+ }
372
+ return lines.join("\n");
373
+ }
374
+
189
375
  // ../src/sdk/index.ts
190
376
  var WhisperError = class extends Error {
191
377
  code;
@@ -216,11 +402,24 @@ function getBackoffDelay(attempt, base, max) {
216
402
  function isLikelyProjectId(projectRef) {
217
403
  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);
218
404
  }
405
+ function normalizeBaseUrl(url) {
406
+ let normalized = url.trim().replace(/\/+$/, "");
407
+ normalized = normalized.replace(/\/api\/v1$/i, "");
408
+ normalized = normalized.replace(/\/v1$/i, "");
409
+ normalized = normalized.replace(/\/api$/i, "");
410
+ return normalized;
411
+ }
412
+ function normalizeEndpoint(endpoint) {
413
+ const withLeadingSlash = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
414
+ if (/^\/api\/v1(\/|$)/i.test(withLeadingSlash)) {
415
+ return withLeadingSlash.replace(/^\/api/i, "");
416
+ }
417
+ return withLeadingSlash;
418
+ }
219
419
  var WhisperContext = class _WhisperContext {
220
420
  apiKey;
221
421
  baseUrl;
222
422
  defaultProject;
223
- orgId;
224
423
  timeoutMs;
225
424
  retryConfig;
226
425
  projectRefToId = /* @__PURE__ */ new Map();
@@ -234,9 +433,8 @@ var WhisperContext = class _WhisperContext {
234
433
  });
235
434
  }
236
435
  this.apiKey = config.apiKey;
237
- this.baseUrl = config.baseUrl || "https://context.usewhisper.dev";
436
+ this.baseUrl = normalizeBaseUrl(config.baseUrl || "https://context.usewhisper.dev");
238
437
  this.defaultProject = config.project;
239
- this.orgId = config.orgId;
240
438
  this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
241
439
  this.retryConfig = {
242
440
  maxAttempts: config.retry?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS,
@@ -249,7 +447,6 @@ var WhisperContext = class _WhisperContext {
249
447
  apiKey: this.apiKey,
250
448
  baseUrl: this.baseUrl,
251
449
  project,
252
- orgId: this.orgId,
253
450
  timeoutMs: this.timeoutMs,
254
451
  retry: this.retryConfig
255
452
  });
@@ -366,20 +563,28 @@ var WhisperContext = class _WhisperContext {
366
563
  }
367
564
  async request(endpoint, options = {}) {
368
565
  const maxAttempts = Math.max(1, this.retryConfig.maxAttempts);
566
+ const normalizedEndpoint = normalizeEndpoint(endpoint);
369
567
  let lastError;
370
568
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
371
569
  const controller = new AbortController();
372
570
  const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
373
571
  try {
374
- const response = await fetch(`${this.baseUrl}${endpoint}`, {
572
+ const headers = {
573
+ "Content-Type": "application/json",
574
+ ...options.headers
575
+ };
576
+ const hasAuthHeader = Object.keys(headers).some((k) => k.toLowerCase() === "authorization");
577
+ const hasApiKeyHeader = Object.keys(headers).some((k) => k.toLowerCase() === "x-api-key");
578
+ if (!hasAuthHeader) {
579
+ headers.Authorization = `Bearer ${this.apiKey}`;
580
+ }
581
+ if (!hasApiKeyHeader) {
582
+ headers["X-API-Key"] = this.apiKey;
583
+ }
584
+ const response = await fetch(`${this.baseUrl}${normalizedEndpoint}`, {
375
585
  ...options,
376
586
  signal: controller.signal,
377
- headers: {
378
- Authorization: `Bearer ${this.apiKey}`,
379
- "Content-Type": "application/json",
380
- ...this.orgId ? { "X-Whisper-Org-Id": this.orgId } : {},
381
- ...options.headers
382
- }
587
+ headers
383
588
  });
384
589
  clearTimeout(timeout);
385
590
  if (!response.ok) {
@@ -568,6 +773,34 @@ var WhisperContext = class _WhisperContext {
568
773
  return { id, success: true, path: "legacy", fallback_used: true };
569
774
  });
570
775
  }
776
+ async addMemoriesBulk(params) {
777
+ const projectRef = this.getRequiredProject(params.project);
778
+ return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/bulk", {
779
+ method: "POST",
780
+ body: JSON.stringify({ ...params, project })
781
+ }));
782
+ }
783
+ async extractMemories(params) {
784
+ const projectRef = this.getRequiredProject(params.project);
785
+ return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/extract", {
786
+ method: "POST",
787
+ body: JSON.stringify({ ...params, project })
788
+ }));
789
+ }
790
+ async extractSessionMemories(params) {
791
+ const projectRef = this.getRequiredProject(params.project);
792
+ return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/extract/session", {
793
+ method: "POST",
794
+ body: JSON.stringify({
795
+ ...params,
796
+ project,
797
+ messages: params.messages.map((m) => ({
798
+ ...m,
799
+ timestamp: m.timestamp || (/* @__PURE__ */ new Date()).toISOString()
800
+ }))
801
+ })
802
+ }));
803
+ }
571
804
  async searchMemories(params) {
572
805
  const projectRef = this.getRequiredProject(params.project);
573
806
  return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/search", {
@@ -640,6 +873,26 @@ var WhisperContext = class _WhisperContext {
640
873
  async getMemoryRelations(memoryId) {
641
874
  return this.request(`/v1/memory/${memoryId}/relations`);
642
875
  }
876
+ async getMemoryGraph(params) {
877
+ const project = await this.resolveProjectId(this.getRequiredProject(params.project));
878
+ const query = new URLSearchParams({
879
+ project,
880
+ ...params.user_id && { user_id: params.user_id },
881
+ ...params.session_id && { session_id: params.session_id },
882
+ ...params.include_inactive !== void 0 && { include_inactive: String(params.include_inactive) },
883
+ ...params.limit !== void 0 && { limit: String(params.limit) }
884
+ });
885
+ return this.request(`/v1/memory/graph?${query}`);
886
+ }
887
+ async getConversationGraph(params) {
888
+ const project = await this.resolveProjectId(this.getRequiredProject(params.project));
889
+ const query = new URLSearchParams({
890
+ project,
891
+ ...params.include_inactive !== void 0 && { include_inactive: String(params.include_inactive) },
892
+ ...params.limit !== void 0 && { limit: String(params.limit) }
893
+ });
894
+ return this.request(`/v1/memory/graph/conversation/${params.session_id}?${query}`);
895
+ }
643
896
  async oracleSearch(params) {
644
897
  const project = await this.resolveProjectId(this.getRequiredProject(params.project));
645
898
  return this.request("/v1/oracle/search", {
@@ -764,6 +1017,9 @@ var WhisperContext = class _WhisperContext {
764
1017
  };
765
1018
  memory = {
766
1019
  add: (params) => this.addMemory(params),
1020
+ addBulk: (params) => this.addMemoriesBulk(params),
1021
+ extract: (params) => this.extractMemories(params),
1022
+ extractSession: (params) => this.extractSessionMemories(params),
767
1023
  search: (params) => this.searchMemories(params),
768
1024
  searchSOTA: (params) => this.searchMemoriesSOTA(params),
769
1025
  ingestSession: (params) => this.ingestSession(params),
@@ -773,6 +1029,8 @@ var WhisperContext = class _WhisperContext {
773
1029
  update: (memoryId, params) => this.updateMemory(memoryId, params),
774
1030
  delete: (memoryId) => this.deleteMemory(memoryId),
775
1031
  getRelations: (memoryId) => this.getMemoryRelations(memoryId),
1032
+ getGraph: (params) => this.getMemoryGraph(params),
1033
+ getConversationGraph: (params) => this.getConversationGraph(params),
776
1034
  consolidate: (params) => this.consolidateMemories(params),
777
1035
  updateDecay: (params) => this.updateImportanceDecay(params),
778
1036
  getImportanceStats: (project) => this.getImportanceStats(project)
@@ -803,7 +1061,10 @@ var index_default = WhisperContext;
803
1061
  // Annotate the CommonJS export names for ESM import in node:
804
1062
  0 && (module.exports = {
805
1063
  Whisper,
1064
+ WhisperAgentMiddleware,
806
1065
  WhisperContext,
807
1066
  WhisperDefault,
808
- WhisperError
1067
+ WhisperError,
1068
+ createAgentMiddleware,
1069
+ memoryGraphToMermaid
809
1070
  });