@usewhisper/sdk 2.1.0 → 2.2.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.
Files changed (5) hide show
  1. package/index.d.mts +124 -7
  2. package/index.d.ts +124 -7
  3. package/index.js +287 -42
  4. package/index.mjs +285 -41
  5. package/package.json +1 -1
package/index.mjs CHANGED
@@ -117,7 +117,7 @@ ${context}` : "",
117
117
  if (extractedMemories.length > 0) {
118
118
  const bulk = await this.client.addMemoriesBulk({
119
119
  project: options?.project ?? this.options.project,
120
- async: false,
120
+ write_mode: "async",
121
121
  memories: extractedMemories.map((m) => ({
122
122
  content: m.content,
123
123
  memory_type: m.memoryType,
@@ -185,6 +185,10 @@ ${context}` : "",
185
185
  extracted: result?.memories_created ?? 0
186
186
  };
187
187
  } catch (error) {
188
+ const fallback = await this.fallbackCaptureViaAddMemory(messages, options);
189
+ if (fallback.success) {
190
+ return fallback;
191
+ }
188
192
  console.error("[Whisper] Session capture failed:", error);
189
193
  return { success: false, extracted: 0 };
190
194
  }
@@ -242,6 +246,28 @@ User: ${params.userMessage}` : params.userMessage;
242
246
  }
243
247
  return Array.from(new Set(ids));
244
248
  }
249
+ async fallbackCaptureViaAddMemory(messages, options) {
250
+ const userMessages = messages.filter((m) => m.role === "user").map((m) => (m.content || "").trim()).filter((content) => content.length >= 5).slice(-2);
251
+ if (userMessages.length === 0) {
252
+ return { success: false, extracted: 0 };
253
+ }
254
+ let extracted = 0;
255
+ for (const content of userMessages) {
256
+ try {
257
+ await this.client.addMemory({
258
+ project: options?.project ?? this.options.project,
259
+ content,
260
+ memory_type: "factual",
261
+ user_id: options?.userId ?? this.userId,
262
+ session_id: options?.sessionId ?? this.sessionId,
263
+ allow_legacy_fallback: true
264
+ });
265
+ extracted += 1;
266
+ } catch {
267
+ }
268
+ }
269
+ return { success: extracted > 0, extracted };
270
+ }
245
271
  };
246
272
  var whisper_agent_default = Whisper;
247
273
 
@@ -314,6 +340,31 @@ function createAgentMiddleware(config) {
314
340
  return new WhisperAgentMiddleware(config);
315
341
  }
316
342
 
343
+ // ../src/sdk/graph-utils.ts
344
+ function sanitizeId(id) {
345
+ return `n_${id.replace(/[^a-zA-Z0-9_]/g, "_")}`;
346
+ }
347
+ function shortLabel(input, max = 48) {
348
+ const text = (input || "").replace(/\s+/g, " ").trim();
349
+ if (text.length <= max) return text;
350
+ return `${text.slice(0, max - 3)}...`;
351
+ }
352
+ function memoryGraphToMermaid(graph) {
353
+ const lines = ["flowchart LR"];
354
+ for (const node of graph.nodes || []) {
355
+ const sid = sanitizeId(node.id);
356
+ const label = shortLabel(node.label || node.id);
357
+ lines.push(` ${sid}["${label.replace(/"/g, '\\"')}"]`);
358
+ }
359
+ for (const edge of graph.edges || []) {
360
+ const s = sanitizeId(edge.source);
361
+ const t = sanitizeId(edge.target);
362
+ const rel = shortLabel(edge.type || "rel", 18).replace(/"/g, '\\"');
363
+ lines.push(` ${s} -->|${rel}| ${t}`);
364
+ }
365
+ return lines.join("\n");
366
+ }
367
+
317
368
  // ../src/sdk/index.ts
318
369
  var WhisperError = class extends Error {
319
370
  code;
@@ -358,6 +409,10 @@ function normalizeEndpoint(endpoint) {
358
409
  }
359
410
  return withLeadingSlash;
360
411
  }
412
+ function isProjectNotFoundMessage(message) {
413
+ const normalized = message.toLowerCase();
414
+ return normalized.includes("project not found") || normalized.includes("no project found") || normalized.includes("project does not exist");
415
+ }
361
416
  var WhisperContext = class _WhisperContext {
362
417
  apiKey;
363
418
  baseUrl;
@@ -464,9 +519,17 @@ var WhisperContext = class _WhisperContext {
464
519
  return Array.from(candidates).filter(Boolean);
465
520
  }
466
521
  async withProjectRefFallback(projectRef, execute) {
522
+ try {
523
+ return await execute(projectRef);
524
+ } catch (error) {
525
+ if (!(error instanceof WhisperError) || error.code !== "PROJECT_NOT_FOUND") {
526
+ throw error;
527
+ }
528
+ }
467
529
  const refs = await this.getProjectRefCandidates(projectRef);
468
530
  let lastError;
469
531
  for (const ref of refs) {
532
+ if (ref === projectRef) continue;
470
533
  try {
471
534
  return await execute(ref);
472
535
  } catch (error) {
@@ -489,7 +552,7 @@ var WhisperContext = class _WhisperContext {
489
552
  if (status === 401 || /api key|unauthorized|forbidden/i.test(message)) {
490
553
  return { code: "INVALID_API_KEY", retryable: false };
491
554
  }
492
- if (status === 404 || /project not found/i.test(message)) {
555
+ if (status === 404 && isProjectNotFoundMessage(message)) {
493
556
  return { code: "PROJECT_NOT_FOUND", retryable: false };
494
557
  }
495
558
  if (status === 408) {
@@ -503,6 +566,16 @@ var WhisperContext = class _WhisperContext {
503
566
  }
504
567
  return { code: "REQUEST_FAILED", retryable: false };
505
568
  }
569
+ isEndpointNotFoundError(error) {
570
+ if (!(error instanceof WhisperError)) {
571
+ return false;
572
+ }
573
+ if (error.status !== 404) {
574
+ return false;
575
+ }
576
+ const message = (error.message || "").toLowerCase();
577
+ return !isProjectNotFoundMessage(message);
578
+ }
506
579
  async request(endpoint, options = {}) {
507
580
  const maxAttempts = Math.max(1, this.retryConfig.maxAttempts);
508
581
  const normalizedEndpoint = normalizeEndpoint(endpoint);
@@ -536,7 +609,11 @@ var WhisperContext = class _WhisperContext {
536
609
  } catch {
537
610
  payload = await response.text().catch(() => "");
538
611
  }
539
- const message = typeof payload === "string" ? payload : payload?.error || payload?.message || `HTTP ${response.status}: ${response.statusText}`;
612
+ let message = typeof payload === "string" ? payload : payload?.error || payload?.message || `HTTP ${response.status}: ${response.statusText}`;
613
+ if (response.status === 404 && !isProjectNotFoundMessage(message)) {
614
+ const endpointHint = `${this.baseUrl}${normalizedEndpoint}`;
615
+ message = `Endpoint not found at ${endpointHint}. This deployment may not support this API route.`;
616
+ }
540
617
  const { code, retryable } = this.classifyError(response.status, message);
541
618
  const err = new WhisperError({
542
619
  code,
@@ -679,13 +756,18 @@ var WhisperContext = class _WhisperContext {
679
756
  session_id: params.session_id,
680
757
  agent_id: params.agent_id,
681
758
  importance: params.importance,
682
- metadata: params.metadata
759
+ metadata: params.metadata,
760
+ async: params.async,
761
+ write_mode: params.write_mode
683
762
  })
684
763
  });
685
- const id2 = direct?.memory?.id || direct?.id || direct?.memory_id;
764
+ const id2 = direct?.memory?.id || direct?.id || direct?.memory_id || direct?.job_id;
686
765
  if (id2) {
687
766
  return { id: id2, success: true, path: "sota", fallback_used: false };
688
767
  }
768
+ if (direct?.success === true) {
769
+ return { id: "", success: true, path: "sota", fallback_used: false };
770
+ }
689
771
  } catch (error) {
690
772
  if (params.allow_legacy_fallback === false) {
691
773
  throw error;
@@ -717,10 +799,40 @@ var WhisperContext = class _WhisperContext {
717
799
  }
718
800
  async addMemoriesBulk(params) {
719
801
  const projectRef = this.getRequiredProject(params.project);
720
- return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/bulk", {
721
- method: "POST",
722
- body: JSON.stringify({ ...params, project })
723
- }));
802
+ return this.withProjectRefFallback(projectRef, async (project) => {
803
+ try {
804
+ return await this.request("/v1/memory/bulk", {
805
+ method: "POST",
806
+ body: JSON.stringify({ ...params, project })
807
+ });
808
+ } catch (error) {
809
+ if (!this.isEndpointNotFoundError(error)) {
810
+ throw error;
811
+ }
812
+ const created = await Promise.all(
813
+ params.memories.map(
814
+ (memory) => this.addMemory({
815
+ project,
816
+ content: memory.content,
817
+ memory_type: memory.memory_type,
818
+ user_id: memory.user_id,
819
+ session_id: memory.session_id,
820
+ agent_id: memory.agent_id,
821
+ importance: memory.importance,
822
+ metadata: memory.metadata,
823
+ allow_legacy_fallback: true
824
+ })
825
+ )
826
+ );
827
+ return {
828
+ success: true,
829
+ created: created.length,
830
+ memories: created,
831
+ path: "legacy",
832
+ fallback_used: true
833
+ };
834
+ }
835
+ });
724
836
  }
725
837
  async extractMemories(params) {
726
838
  const projectRef = this.getRequiredProject(params.project);
@@ -745,17 +857,48 @@ var WhisperContext = class _WhisperContext {
745
857
  }
746
858
  async searchMemories(params) {
747
859
  const projectRef = this.getRequiredProject(params.project);
748
- return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/search", {
749
- method: "POST",
750
- body: JSON.stringify({
751
- query: params.query,
752
- project,
753
- user_id: params.user_id,
754
- session_id: params.session_id,
755
- memory_types: params.memory_type ? [params.memory_type] : void 0,
756
- top_k: params.top_k || 10
757
- })
758
- }));
860
+ return this.withProjectRefFallback(projectRef, async (project) => {
861
+ try {
862
+ return await this.request("/v1/memory/search", {
863
+ method: "POST",
864
+ body: JSON.stringify({
865
+ query: params.query,
866
+ project,
867
+ user_id: params.user_id,
868
+ session_id: params.session_id,
869
+ memory_types: params.memory_type ? [params.memory_type] : void 0,
870
+ top_k: params.top_k || 10,
871
+ profile: params.profile,
872
+ include_pending: params.include_pending
873
+ })
874
+ });
875
+ } catch (error) {
876
+ if (!this.isEndpointNotFoundError(error)) {
877
+ throw error;
878
+ }
879
+ const legacyTypeMap = {
880
+ factual: "factual",
881
+ preference: "semantic",
882
+ event: "episodic",
883
+ relationship: "semantic",
884
+ opinion: "semantic",
885
+ goal: "semantic",
886
+ instruction: "procedural"
887
+ };
888
+ return this.request("/v1/memories/search", {
889
+ method: "POST",
890
+ body: JSON.stringify({
891
+ query: params.query,
892
+ project,
893
+ user_id: params.user_id,
894
+ session_id: params.session_id,
895
+ agent_id: params.agent_id,
896
+ memory_type: params.memory_type ? legacyTypeMap[params.memory_type] : void 0,
897
+ top_k: params.top_k || 10
898
+ })
899
+ });
900
+ }
901
+ });
759
902
  }
760
903
  async createApiKey(params) {
761
904
  return this.request("/v1/keys", {
@@ -771,10 +914,29 @@ var WhisperContext = class _WhisperContext {
771
914
  }
772
915
  async searchMemoriesSOTA(params) {
773
916
  const projectRef = this.getRequiredProject(params.project);
774
- return this.withProjectRefFallback(projectRef, (project) => this.request("/v1/memory/search", {
775
- method: "POST",
776
- body: JSON.stringify({ ...params, project })
777
- }));
917
+ return this.withProjectRefFallback(projectRef, async (project) => {
918
+ try {
919
+ return await this.request("/v1/memory/search", {
920
+ method: "POST",
921
+ body: JSON.stringify({ ...params, project })
922
+ });
923
+ } catch (error) {
924
+ if (!this.isEndpointNotFoundError(error)) {
925
+ throw error;
926
+ }
927
+ const firstType = params.memory_types?.[0];
928
+ return this.searchMemories({
929
+ project,
930
+ query: params.query,
931
+ user_id: params.user_id,
932
+ session_id: params.session_id,
933
+ memory_type: firstType,
934
+ top_k: params.top_k,
935
+ profile: params.profile,
936
+ include_pending: params.include_pending
937
+ });
938
+ }
939
+ });
778
940
  }
779
941
  async ingestSession(params) {
780
942
  const projectRef = this.getRequiredProject(params.project);
@@ -784,37 +946,116 @@ var WhisperContext = class _WhisperContext {
784
946
  }));
785
947
  }
786
948
  async getSessionMemories(params) {
787
- const project = await this.resolveProjectId(this.getRequiredProject(params.project));
788
- const query = new URLSearchParams({
789
- project,
790
- ...params.limit && { limit: params.limit.toString() },
791
- ...params.since_date && { since_date: params.since_date }
949
+ const projectRef = this.getRequiredProject(params.project);
950
+ return this.withProjectRefFallback(projectRef, async (project) => {
951
+ const query = new URLSearchParams({
952
+ project,
953
+ ...params.limit && { limit: params.limit.toString() },
954
+ ...params.since_date && { since_date: params.since_date },
955
+ ...params.include_pending !== void 0 && { include_pending: String(params.include_pending) }
956
+ });
957
+ try {
958
+ return await this.request(`/v1/memory/session/${params.session_id}?${query}`);
959
+ } catch (error) {
960
+ if (!this.isEndpointNotFoundError(error)) {
961
+ throw error;
962
+ }
963
+ return { memories: [], count: 0 };
964
+ }
792
965
  });
793
- return this.request(`/v1/memory/session/${params.session_id}?${query}`);
794
966
  }
795
967
  async getUserProfile(params) {
796
- const project = await this.resolveProjectId(this.getRequiredProject(params.project));
797
- const query = new URLSearchParams({
798
- project,
799
- ...params.memory_types && { memory_types: params.memory_types }
968
+ const projectRef = this.getRequiredProject(params.project);
969
+ return this.withProjectRefFallback(projectRef, async (project) => {
970
+ const query = new URLSearchParams({
971
+ project,
972
+ ...params.memory_types && { memory_types: params.memory_types },
973
+ ...params.include_pending !== void 0 && { include_pending: String(params.include_pending) }
974
+ });
975
+ try {
976
+ return await this.request(`/v1/memory/profile/${params.user_id}?${query}`);
977
+ } catch (error) {
978
+ if (!this.isEndpointNotFoundError(error)) {
979
+ throw error;
980
+ }
981
+ const legacyQuery = new URLSearchParams({
982
+ project,
983
+ user_id: params.user_id,
984
+ limit: "200"
985
+ });
986
+ const legacy = await this.request(`/v1/memories?${legacyQuery}`);
987
+ const memories = Array.isArray(legacy?.memories) ? legacy.memories : [];
988
+ return {
989
+ user_id: params.user_id,
990
+ memories,
991
+ count: memories.length
992
+ };
993
+ }
800
994
  });
801
- return this.request(`/v1/memory/profile/${params.user_id}?${query}`);
802
995
  }
803
996
  async getMemoryVersions(memoryId) {
804
997
  return this.request(`/v1/memory/${memoryId}/versions`);
805
998
  }
806
999
  async updateMemory(memoryId, params) {
807
- return this.request(`/v1/memory/${memoryId}`, {
808
- method: "PUT",
809
- body: JSON.stringify(params)
810
- });
1000
+ try {
1001
+ return await this.request(`/v1/memory/${memoryId}`, {
1002
+ method: "PUT",
1003
+ body: JSON.stringify(params)
1004
+ });
1005
+ } catch (error) {
1006
+ if (!this.isEndpointNotFoundError(error)) {
1007
+ throw error;
1008
+ }
1009
+ const legacy = await this.request(`/v1/memories/${memoryId}`, {
1010
+ method: "PUT",
1011
+ body: JSON.stringify({
1012
+ content: params.content
1013
+ })
1014
+ });
1015
+ return {
1016
+ success: true,
1017
+ new_memory_id: legacy?.id || memoryId,
1018
+ old_memory_id: memoryId
1019
+ };
1020
+ }
811
1021
  }
812
1022
  async deleteMemory(memoryId) {
813
- return this.request(`/v1/memory/${memoryId}`, { method: "DELETE" });
1023
+ try {
1024
+ return await this.request(`/v1/memory/${memoryId}`, { method: "DELETE" });
1025
+ } catch (error) {
1026
+ if (!this.isEndpointNotFoundError(error)) {
1027
+ throw error;
1028
+ }
1029
+ await this.request(`/v1/memories/${memoryId}`, { method: "DELETE" });
1030
+ return {
1031
+ success: true,
1032
+ deleted: memoryId
1033
+ };
1034
+ }
814
1035
  }
815
1036
  async getMemoryRelations(memoryId) {
816
1037
  return this.request(`/v1/memory/${memoryId}/relations`);
817
1038
  }
1039
+ async getMemoryGraph(params) {
1040
+ const project = await this.resolveProjectId(this.getRequiredProject(params.project));
1041
+ const query = new URLSearchParams({
1042
+ project,
1043
+ ...params.user_id && { user_id: params.user_id },
1044
+ ...params.session_id && { session_id: params.session_id },
1045
+ ...params.include_inactive !== void 0 && { include_inactive: String(params.include_inactive) },
1046
+ ...params.limit !== void 0 && { limit: String(params.limit) }
1047
+ });
1048
+ return this.request(`/v1/memory/graph?${query}`);
1049
+ }
1050
+ async getConversationGraph(params) {
1051
+ const project = await this.resolveProjectId(this.getRequiredProject(params.project));
1052
+ const query = new URLSearchParams({
1053
+ project,
1054
+ ...params.include_inactive !== void 0 && { include_inactive: String(params.include_inactive) },
1055
+ ...params.limit !== void 0 && { limit: String(params.limit) }
1056
+ });
1057
+ return this.request(`/v1/memory/graph/conversation/${params.session_id}?${query}`);
1058
+ }
818
1059
  async oracleSearch(params) {
819
1060
  const project = await this.resolveProjectId(this.getRequiredProject(params.project));
820
1061
  return this.request("/v1/oracle/search", {
@@ -951,6 +1192,8 @@ var WhisperContext = class _WhisperContext {
951
1192
  update: (memoryId, params) => this.updateMemory(memoryId, params),
952
1193
  delete: (memoryId) => this.deleteMemory(memoryId),
953
1194
  getRelations: (memoryId) => this.getMemoryRelations(memoryId),
1195
+ getGraph: (params) => this.getMemoryGraph(params),
1196
+ getConversationGraph: (params) => this.getConversationGraph(params),
954
1197
  consolidate: (params) => this.consolidateMemories(params),
955
1198
  updateDecay: (params) => this.updateImportanceDecay(params),
956
1199
  getImportanceStats: (project) => this.getImportanceStats(project)
@@ -985,5 +1228,6 @@ export {
985
1228
  whisper_agent_default as WhisperDefault,
986
1229
  WhisperError,
987
1230
  createAgentMiddleware,
988
- index_default as default
1231
+ index_default as default,
1232
+ memoryGraphToMermaid
989
1233
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usewhisper/sdk",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "scripts": {
5
5
  "build": "tsup ../src/sdk/index.ts --format esm,cjs --dts --out-dir .",
6
6
  "prepublishOnly": "npm run build"