@usewhisper/sdk 3.5.0 → 3.6.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
@@ -379,368 +379,83 @@ var RuntimeClient = class {
379
379
  }
380
380
  };
381
381
 
382
- // ../src/sdk/whisper-agent.ts
383
- var DEPRECATION_WARNINGS = /* @__PURE__ */ new Set();
384
- function warnDeprecatedOnce(key, message) {
385
- if (DEPRECATION_WARNINGS.has(key)) return;
386
- DEPRECATION_WARNINGS.add(key);
387
- if (typeof console !== "undefined" && typeof console.warn === "function") {
388
- console.warn(message);
382
+ // ../src/sdk/core/cache.ts
383
+ var SearchResponseCache = class {
384
+ ttlMs;
385
+ capacity;
386
+ byKey = /* @__PURE__ */ new Map();
387
+ scopeIndex = /* @__PURE__ */ new Map();
388
+ constructor(ttlMs = 7e3, capacity = 500) {
389
+ this.ttlMs = Math.max(1e3, ttlMs);
390
+ this.capacity = Math.max(10, capacity);
389
391
  }
390
- }
391
- var Whisper = class {
392
- client;
393
- options;
394
- sessionId;
395
- userId;
396
- constructor(options) {
397
- if (!options.apiKey) {
398
- throw new Error("API key is required");
399
- }
400
- const clientConfig = {
401
- apiKey: options.apiKey,
402
- baseUrl: options.baseUrl,
403
- project: options.project || "default"
404
- };
405
- if (options.timeoutMs) clientConfig.timeoutMs = options.timeoutMs;
406
- if (options.retry) clientConfig.retry = options.retry;
407
- this.client = new WhisperContext(clientConfig);
408
- warnDeprecatedOnce(
409
- "whisper_agent_wrapper",
410
- "[Whisper SDK] Whisper wrapper is supported for v2 compatibility. Prefer WhisperClient for new integrations."
411
- );
412
- const finalRetry = options.retry || { maxAttempts: 3, baseDelayMs: 250, maxDelayMs: 2e3 };
413
- this.options = {
414
- apiKey: options.apiKey,
415
- baseUrl: options.baseUrl || "https://context.usewhisper.dev",
416
- project: options.project || "default",
417
- timeoutMs: options.timeoutMs || 15e3,
418
- retry: finalRetry,
419
- contextLimit: options.contextLimit ?? 10,
420
- memoryTypes: options.memoryTypes ?? ["factual", "preference", "event", "goal", "relationship", "opinion", "instruction"],
421
- contextPrefix: options.contextPrefix ?? "Relevant context:",
422
- autoExtract: options.autoExtract ?? true,
423
- autoExtractMinConfidence: options.autoExtractMinConfidence ?? 0.65,
424
- maxMemoriesPerCapture: options.maxMemoriesPerCapture ?? 5
425
- };
392
+ makeScopeKey(project, userId, sessionId) {
393
+ return `${project}:${userId || "_"}:${sessionId || "_"}`;
426
394
  }
427
- /**
428
- * Set session ID for conversation tracking
429
- */
430
- session(sessionId) {
431
- this.sessionId = sessionId;
432
- return this;
395
+ makeKey(input) {
396
+ const normalized = {
397
+ project: input.project,
398
+ userId: input.userId || "",
399
+ sessionId: input.sessionId || "",
400
+ query: normalizeQuery(input.query),
401
+ topK: input.topK,
402
+ profile: input.profile,
403
+ includePending: input.includePending
404
+ };
405
+ return `search:${stableHash(JSON.stringify(normalized))}`;
433
406
  }
434
- /**
435
- * Set user ID for user-specific memories
436
- */
437
- user(userId) {
438
- this.userId = userId;
439
- return this;
407
+ get(key) {
408
+ const found = this.byKey.get(key);
409
+ if (!found) return null;
410
+ if (found.expiresAt <= Date.now()) {
411
+ this.deleteByKey(key);
412
+ return null;
413
+ }
414
+ found.touchedAt = Date.now();
415
+ return found.value;
440
416
  }
441
- /**
442
- * Get relevant context BEFORE your LLM call
443
- *
444
- * @param query - What you want to know / user question
445
- * @returns Context string and raw results
446
- *
447
- * @example
448
- * ```typescript
449
- * const { context, results, count } = await whisper.getContext(
450
- * "What are user's preferences?",
451
- * { userId: "user-123" }
452
- * );
453
- *
454
- * // Results: [
455
- * // { content: "User prefers dark mode", type: "preference", score: 0.95 },
456
- * // { content: "Allergic to nuts", type: "factual", score: 0.89 }
457
- * // ]
458
- * ```
459
- */
460
- async getContext(query, options) {
461
- const result = await this.client.query({
462
- project: options?.project ?? this.options.project,
463
- query,
464
- top_k: options?.limit ?? this.options.contextLimit,
465
- include_memories: true,
466
- user_id: options?.userId ?? this.userId,
467
- session_id: options?.sessionId ?? this.sessionId
417
+ set(key, scopeKey, value) {
418
+ this.byKey.set(key, {
419
+ value,
420
+ scopeKey,
421
+ touchedAt: Date.now(),
422
+ expiresAt: Date.now() + this.ttlMs
468
423
  });
469
- const context = result.results.map((r, i) => `[${i + 1}] ${r.content}`).join("\n");
470
- return {
471
- context: context ? `${this.options.contextPrefix}
472
- ${context}` : "",
473
- results: result.results,
474
- count: result.meta.total
475
- };
424
+ if (!this.scopeIndex.has(scopeKey)) {
425
+ this.scopeIndex.set(scopeKey, /* @__PURE__ */ new Set());
426
+ }
427
+ this.scopeIndex.get(scopeKey).add(key);
428
+ this.evictIfNeeded();
476
429
  }
477
- /**
478
- * Remember what happened AFTER your LLM response
479
- *
480
- * Fire-and-forget - doesn't block your response
481
- *
482
- * @param content - What your LLM responded with
483
- * @returns Promise that resolves when stored (or fails silently)
484
- *
485
- * @example
486
- * ```typescript
487
- * const llmResponse = "I've set your theme to dark mode and removed nuts from recommendations.";
488
- *
489
- * await whisper.remember(llmResponse, { userId: "user-123" });
490
- * // → Auto-extracts: "theme set to dark mode", "nut allergy"
491
- * // → Stored as preferences
492
- * ```
493
- */
494
- async remember(content, options) {
495
- if (!content || content.length < 5) {
496
- return { success: false };
430
+ invalidateScope(scopeKey) {
431
+ const keys = this.scopeIndex.get(scopeKey);
432
+ if (!keys || keys.size === 0) {
433
+ return 0;
497
434
  }
498
- try {
499
- if (this.options.autoExtract) {
500
- const extraction = await this.client.extractMemories({
501
- project: options?.project ?? this.options.project,
502
- message: content,
503
- user_id: options?.userId ?? this.userId,
504
- session_id: options?.sessionId ?? this.sessionId,
505
- enable_pattern: true,
506
- enable_inference: true,
507
- min_confidence: this.options.autoExtractMinConfidence
508
- });
509
- const extractedMemories = (extraction.all || []).filter((m) => (m.confidence || 0) >= this.options.autoExtractMinConfidence).slice(0, this.options.maxMemoriesPerCapture);
510
- if (extractedMemories.length > 0) {
511
- const bulk = await this.client.addMemoriesBulk({
512
- project: options?.project ?? this.options.project,
513
- write_mode: "async",
514
- memories: extractedMemories.map((m) => ({
515
- content: m.content,
516
- memory_type: m.memoryType,
517
- user_id: options?.userId ?? this.userId,
518
- session_id: options?.sessionId ?? this.sessionId,
519
- importance: Math.max(0.5, Math.min(1, m.confidence || 0.7)),
520
- confidence: m.confidence || 0.7,
521
- entity_mentions: m.entityMentions || [],
522
- event_date: m.eventDate || void 0,
523
- metadata: {
524
- extracted: true,
525
- extraction_method: extraction.extractionMethod,
526
- extraction_reasoning: m.reasoning,
527
- inferred: Boolean(m.inferred)
528
- }
529
- }))
530
- });
531
- const memoryIds = this.extractMemoryIdsFromBulkResponse(bulk);
532
- return {
533
- success: true,
534
- memoryId: memoryIds[0],
535
- memoryIds: memoryIds.length > 0 ? memoryIds : void 0,
536
- extracted: extractedMemories.length
537
- };
538
- }
539
- }
540
- const result = await this.client.addMemory({
541
- project: options?.project ?? this.options.project,
542
- content,
543
- user_id: options?.userId ?? this.userId,
544
- session_id: options?.sessionId ?? this.sessionId
545
- });
546
- return {
547
- success: true,
548
- memoryId: result?.id
549
- };
550
- } catch (error) {
551
- console.error("[Whisper] Remember failed:", error);
552
- return { success: false };
435
+ const toDelete = Array.from(keys);
436
+ for (const key of toDelete) {
437
+ this.deleteByKey(key);
553
438
  }
439
+ this.scopeIndex.delete(scopeKey);
440
+ return toDelete.length;
554
441
  }
555
- /**
556
- * Alias for remember() - same thing
557
- */
558
- async capture(content, options) {
559
- return this.remember(content, options);
442
+ evictIfNeeded() {
443
+ if (this.byKey.size <= this.capacity) return;
444
+ const ordered = Array.from(this.byKey.entries()).sort((a, b) => a[1].touchedAt - b[1].touchedAt);
445
+ const removeCount = this.byKey.size - this.capacity;
446
+ for (let i = 0; i < removeCount; i += 1) {
447
+ this.deleteByKey(ordered[i][0]);
448
+ }
560
449
  }
561
- /**
562
- * Capture from multiple messages (e.g., full conversation)
563
- */
564
- async captureSession(messages, options) {
565
- try {
566
- const result = await this.client.ingestSession({
567
- project: options?.project ?? this.options.project,
568
- session_id: options?.sessionId ?? this.sessionId ?? "default",
569
- user_id: options?.userId ?? this.userId,
570
- messages: messages.filter((m) => m.role !== "system").map((m) => ({
571
- role: m.role,
572
- content: m.content,
573
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
574
- }))
575
- });
576
- return {
577
- success: true,
578
- extracted: result?.memories_created ?? 0
579
- };
580
- } catch (error) {
581
- const fallback = await this.fallbackCaptureViaAddMemory(messages, options);
582
- if (fallback.success) {
583
- return fallback;
584
- }
585
- console.error("[Whisper] Session capture failed:", error);
586
- return { success: false, extracted: 0 };
587
- }
588
- }
589
- /**
590
- * Run a full agent turn with automatic memory read (before) + write (after).
591
- */
592
- async runTurn(params) {
593
- const contextResult = await this.getContext(params.userMessage, {
594
- userId: params.userId,
595
- sessionId: params.sessionId,
596
- project: params.project,
597
- limit: params.limit
598
- });
599
- const prompt = contextResult.context ? `${contextResult.context}
600
-
601
- User: ${params.userMessage}` : params.userMessage;
602
- const response = await params.generate(prompt);
603
- const captureResult = await this.captureSession(
604
- [
605
- { role: "user", content: params.userMessage },
606
- { role: "assistant", content: response }
607
- ],
608
- {
609
- userId: params.userId,
610
- sessionId: params.sessionId,
611
- project: params.project
612
- }
613
- );
614
- return {
615
- response,
616
- context: contextResult.context,
617
- count: contextResult.count,
618
- extracted: captureResult.extracted
619
- };
620
- }
621
- /**
622
- * Direct access to WhisperContext for advanced usage
623
- */
624
- raw() {
625
- return this.client;
626
- }
627
- extractMemoryIdsFromBulkResponse(bulkResponse) {
628
- const ids = [];
629
- if (Array.isArray(bulkResponse?.memories)) {
630
- for (const memory of bulkResponse.memories) {
631
- if (memory?.id) ids.push(memory.id);
632
- }
633
- }
634
- if (bulkResponse?.memory?.id) {
635
- ids.push(bulkResponse.memory.id);
636
- }
637
- if (bulkResponse?.id) {
638
- ids.push(bulkResponse.id);
639
- }
640
- return Array.from(new Set(ids));
641
- }
642
- async fallbackCaptureViaAddMemory(messages, options) {
643
- const userMessages = messages.filter((m) => m.role === "user").map((m) => (m.content || "").trim()).filter((content) => content.length >= 5).slice(-2);
644
- if (userMessages.length === 0) {
645
- return { success: false, extracted: 0 };
646
- }
647
- let extracted = 0;
648
- for (const content of userMessages) {
649
- try {
650
- await this.client.addMemory({
651
- project: options?.project ?? this.options.project,
652
- content,
653
- memory_type: "factual",
654
- user_id: options?.userId ?? this.userId,
655
- session_id: options?.sessionId ?? this.sessionId,
656
- allow_legacy_fallback: true
657
- });
658
- extracted += 1;
659
- } catch {
660
- }
661
- }
662
- return { success: extracted > 0, extracted };
663
- }
664
- };
665
- var whisper_agent_default = Whisper;
666
-
667
- // ../src/sdk/core/cache.ts
668
- var SearchResponseCache = class {
669
- ttlMs;
670
- capacity;
671
- byKey = /* @__PURE__ */ new Map();
672
- scopeIndex = /* @__PURE__ */ new Map();
673
- constructor(ttlMs = 7e3, capacity = 500) {
674
- this.ttlMs = Math.max(1e3, ttlMs);
675
- this.capacity = Math.max(10, capacity);
676
- }
677
- makeScopeKey(project, userId, sessionId) {
678
- return `${project}:${userId || "_"}:${sessionId || "_"}`;
679
- }
680
- makeKey(input) {
681
- const normalized = {
682
- project: input.project,
683
- userId: input.userId || "",
684
- sessionId: input.sessionId || "",
685
- query: normalizeQuery(input.query),
686
- topK: input.topK,
687
- profile: input.profile,
688
- includePending: input.includePending
689
- };
690
- return `search:${stableHash(JSON.stringify(normalized))}`;
691
- }
692
- get(key) {
693
- const found = this.byKey.get(key);
694
- if (!found) return null;
695
- if (found.expiresAt <= Date.now()) {
696
- this.deleteByKey(key);
697
- return null;
698
- }
699
- found.touchedAt = Date.now();
700
- return found.value;
701
- }
702
- set(key, scopeKey, value) {
703
- this.byKey.set(key, {
704
- value,
705
- scopeKey,
706
- touchedAt: Date.now(),
707
- expiresAt: Date.now() + this.ttlMs
708
- });
709
- if (!this.scopeIndex.has(scopeKey)) {
710
- this.scopeIndex.set(scopeKey, /* @__PURE__ */ new Set());
711
- }
712
- this.scopeIndex.get(scopeKey).add(key);
713
- this.evictIfNeeded();
714
- }
715
- invalidateScope(scopeKey) {
716
- const keys = this.scopeIndex.get(scopeKey);
717
- if (!keys || keys.size === 0) {
718
- return 0;
719
- }
720
- const toDelete = Array.from(keys);
721
- for (const key of toDelete) {
722
- this.deleteByKey(key);
723
- }
724
- this.scopeIndex.delete(scopeKey);
725
- return toDelete.length;
726
- }
727
- evictIfNeeded() {
728
- if (this.byKey.size <= this.capacity) return;
729
- const ordered = Array.from(this.byKey.entries()).sort((a, b) => a[1].touchedAt - b[1].touchedAt);
730
- const removeCount = this.byKey.size - this.capacity;
731
- for (let i = 0; i < removeCount; i += 1) {
732
- this.deleteByKey(ordered[i][0]);
733
- }
734
- }
735
- deleteByKey(key) {
736
- const found = this.byKey.get(key);
737
- if (!found) return;
738
- this.byKey.delete(key);
739
- const scopeKeys = this.scopeIndex.get(found.scopeKey);
740
- if (!scopeKeys) return;
741
- scopeKeys.delete(key);
742
- if (scopeKeys.size === 0) {
743
- this.scopeIndex.delete(found.scopeKey);
450
+ deleteByKey(key) {
451
+ const found = this.byKey.get(key);
452
+ if (!found) return;
453
+ this.byKey.delete(key);
454
+ const scopeKeys = this.scopeIndex.get(found.scopeKey);
455
+ if (!scopeKeys) return;
456
+ scopeKeys.delete(key);
457
+ if (scopeKeys.size === 0) {
458
+ this.scopeIndex.delete(found.scopeKey);
744
459
  }
745
460
  }
746
461
  };
@@ -2333,165 +2048,469 @@ var WhisperClient = class _WhisperClient {
2333
2048
  retryable: false
2334
2049
  });
2335
2050
  }
2336
- return resolved;
2051
+ return resolved;
2052
+ }
2053
+ async refreshProjectCache(force = false) {
2054
+ if (!force && Date.now() < this.projectCacheExpiresAt && this.projectCache.length > 0) {
2055
+ return this.projectCache;
2056
+ }
2057
+ const response = await this.runtimeClient.request({
2058
+ endpoint: "/v1/projects",
2059
+ method: "GET",
2060
+ operation: "get",
2061
+ idempotent: true
2062
+ });
2063
+ this.projectRefToId.clear();
2064
+ this.projectCache = response.data?.projects || [];
2065
+ for (const project of this.projectCache) {
2066
+ this.projectRefToId.set(project.id, project.id);
2067
+ this.projectRefToId.set(project.slug, project.id);
2068
+ this.projectRefToId.set(project.name, project.id);
2069
+ }
2070
+ this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS;
2071
+ return this.projectCache;
2072
+ }
2073
+ async fetchResolvedProject(projectRef) {
2074
+ try {
2075
+ const response = await this.runtimeClient.request({
2076
+ endpoint: `/v1/projects/resolve?project=${encodeURIComponent(projectRef)}`,
2077
+ method: "GET",
2078
+ operation: "get",
2079
+ idempotent: true
2080
+ });
2081
+ return response.data?.resolved || null;
2082
+ } catch (error) {
2083
+ if (error instanceof RuntimeClientError && error.status === 404) {
2084
+ return null;
2085
+ }
2086
+ throw error;
2087
+ }
2088
+ }
2089
+ async resolveProject(projectRef) {
2090
+ const resolvedRef = this.getRequiredProject(projectRef);
2091
+ const cachedProjects = await this.refreshProjectCache(false);
2092
+ const cachedProject = cachedProjects.find(
2093
+ (project) => project.id === resolvedRef || project.slug === resolvedRef || project.name === resolvedRef
2094
+ );
2095
+ if (cachedProject) {
2096
+ return cachedProject;
2097
+ }
2098
+ const resolvedProject = await this.fetchResolvedProject(resolvedRef);
2099
+ if (resolvedProject) {
2100
+ this.projectRefToId.set(resolvedProject.id, resolvedProject.id);
2101
+ this.projectRefToId.set(resolvedProject.slug, resolvedProject.id);
2102
+ this.projectRefToId.set(resolvedProject.name, resolvedProject.id);
2103
+ this.projectCache = [
2104
+ ...this.projectCache.filter((project) => project.id !== resolvedProject.id),
2105
+ resolvedProject
2106
+ ];
2107
+ this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS;
2108
+ return resolvedProject;
2109
+ }
2110
+ if (isLikelyProjectId(resolvedRef)) {
2111
+ return {
2112
+ id: resolvedRef,
2113
+ orgId: "",
2114
+ name: resolvedRef,
2115
+ slug: resolvedRef,
2116
+ createdAt: (/* @__PURE__ */ new Date(0)).toISOString(),
2117
+ updatedAt: (/* @__PURE__ */ new Date(0)).toISOString()
2118
+ };
2119
+ }
2120
+ throw new RuntimeClientError({
2121
+ code: "PROJECT_NOT_FOUND",
2122
+ message: `Project '${resolvedRef}' not found`,
2123
+ retryable: false
2124
+ });
2125
+ }
2126
+ async query(params) {
2127
+ const project = (await this.resolveProject(params.project)).id;
2128
+ const response = await this.runtimeClient.request({
2129
+ endpoint: "/v1/context/query",
2130
+ method: "POST",
2131
+ operation: "search",
2132
+ body: {
2133
+ ...params,
2134
+ project
2135
+ },
2136
+ idempotent: true
2137
+ });
2138
+ return response.data;
2139
+ }
2140
+ async ingestSession(params) {
2141
+ const project = (await this.resolveProject(params.project)).id;
2142
+ const response = await this.runtimeClient.request({
2143
+ endpoint: "/v1/memory/ingest/session",
2144
+ method: "POST",
2145
+ operation: "session",
2146
+ body: {
2147
+ ...params,
2148
+ project
2149
+ }
2150
+ });
2151
+ return response.data;
2152
+ }
2153
+ createAgentRuntime(options = {}) {
2154
+ const baseContext = {
2155
+ workspacePath: options.workspacePath,
2156
+ project: options.project || this.config.project,
2157
+ userId: options.userId,
2158
+ sessionId: options.sessionId,
2159
+ traceId: options.traceId,
2160
+ clientName: options.clientName
2161
+ };
2162
+ return new WhisperAgentRuntime({
2163
+ baseContext,
2164
+ options,
2165
+ adapter: {
2166
+ resolveProject: (project) => this.resolveProject(project),
2167
+ query: (params) => this.query(params),
2168
+ ingestSession: (params) => this.ingestSession(params),
2169
+ getSessionMemories: (params) => this.memory.getSessionMemories(params),
2170
+ getUserProfile: (params) => this.memory.getUserProfile(params),
2171
+ searchMemories: (params) => this.memory.search(params),
2172
+ addMemory: (params) => this.memory.add(params),
2173
+ queueStatus: () => this.queue.status(),
2174
+ flushQueue: () => this.queue.flush()
2175
+ }
2176
+ });
2177
+ }
2178
+ withRunContext(context) {
2179
+ const base = this;
2180
+ return {
2181
+ memory: {
2182
+ add: (params) => base.memory.add({
2183
+ ...params,
2184
+ project: params.project || context.project || base.config.project,
2185
+ user_id: params.user_id || context.userId,
2186
+ session_id: params.session_id || context.sessionId
2187
+ }),
2188
+ search: (params) => base.memory.search({
2189
+ ...params,
2190
+ project: params.project || context.project || base.config.project,
2191
+ user_id: params.user_id || context.userId,
2192
+ session_id: params.session_id || context.sessionId
2193
+ })
2194
+ },
2195
+ session: {
2196
+ event: (params) => base.session.event({
2197
+ ...params,
2198
+ sessionId: params.sessionId || context.sessionId || ""
2199
+ })
2200
+ },
2201
+ queue: base.queue,
2202
+ diagnostics: base.diagnostics
2203
+ };
2204
+ }
2205
+ async shutdown() {
2206
+ await this.writeQueue.stop();
2207
+ }
2208
+ };
2209
+ var whisper_default = WhisperClient;
2210
+
2211
+ // ../src/sdk/whisper-agent.ts
2212
+ var DEPRECATION_WARNINGS = /* @__PURE__ */ new Set();
2213
+ function warnDeprecatedOnce(key, message) {
2214
+ if (DEPRECATION_WARNINGS.has(key)) return;
2215
+ DEPRECATION_WARNINGS.add(key);
2216
+ if (typeof console !== "undefined" && typeof console.warn === "function") {
2217
+ console.warn(message);
2218
+ }
2219
+ }
2220
+ var Whisper = class {
2221
+ client;
2222
+ runtimeClient;
2223
+ options;
2224
+ sessionId;
2225
+ userId;
2226
+ constructor(options) {
2227
+ if (!options.apiKey) {
2228
+ throw new Error("API key is required");
2229
+ }
2230
+ const clientConfig = {
2231
+ apiKey: options.apiKey,
2232
+ baseUrl: options.baseUrl,
2233
+ project: options.project || "default"
2234
+ };
2235
+ if (options.timeoutMs) clientConfig.timeoutMs = options.timeoutMs;
2236
+ if (options.retry) clientConfig.retry = options.retry;
2237
+ this.client = new WhisperContext(clientConfig);
2238
+ this.runtimeClient = new WhisperClient({
2239
+ apiKey: options.apiKey,
2240
+ baseUrl: options.baseUrl,
2241
+ project: options.project || "default"
2242
+ });
2243
+ warnDeprecatedOnce(
2244
+ "whisper_agent_wrapper",
2245
+ "[Whisper SDK] Whisper wrapper is supported for v2 compatibility. Prefer WhisperClient for new integrations."
2246
+ );
2247
+ const finalRetry = options.retry || { maxAttempts: 3, baseDelayMs: 250, maxDelayMs: 2e3 };
2248
+ this.options = {
2249
+ apiKey: options.apiKey,
2250
+ baseUrl: options.baseUrl || "https://context.usewhisper.dev",
2251
+ project: options.project || "default",
2252
+ timeoutMs: options.timeoutMs || 15e3,
2253
+ retry: finalRetry,
2254
+ contextLimit: options.contextLimit ?? 10,
2255
+ memoryTypes: options.memoryTypes ?? ["factual", "preference", "event", "goal", "relationship", "opinion", "instruction"],
2256
+ contextPrefix: options.contextPrefix ?? "Relevant context:",
2257
+ autoExtract: options.autoExtract ?? true,
2258
+ autoExtractMinConfidence: options.autoExtractMinConfidence ?? 0.65,
2259
+ maxMemoriesPerCapture: options.maxMemoriesPerCapture ?? 5
2260
+ };
2261
+ }
2262
+ /**
2263
+ * Set session ID for conversation tracking
2264
+ */
2265
+ session(sessionId) {
2266
+ this.sessionId = sessionId;
2267
+ return this;
2268
+ }
2269
+ /**
2270
+ * Set user ID for user-specific memories
2271
+ */
2272
+ user(userId) {
2273
+ this.userId = userId;
2274
+ return this;
2275
+ }
2276
+ /**
2277
+ * Get relevant context BEFORE your LLM call
2278
+ *
2279
+ * @param query - What you want to know / user question
2280
+ * @returns Context string and raw results
2281
+ *
2282
+ * @example
2283
+ * ```typescript
2284
+ * const { context, results, count } = await whisper.getContext(
2285
+ * "What are user's preferences?",
2286
+ * { userId: "user-123" }
2287
+ * );
2288
+ *
2289
+ * // Results: [
2290
+ * // { content: "User prefers dark mode", type: "preference", score: 0.95 },
2291
+ * // { content: "Allergic to nuts", type: "factual", score: 0.89 }
2292
+ * // ]
2293
+ * ```
2294
+ */
2295
+ async getContext(query, options) {
2296
+ const runtime = this.runtimeClient.createAgentRuntime({
2297
+ project: options?.project ?? this.options.project,
2298
+ userId: options?.userId ?? this.userId,
2299
+ sessionId: options?.sessionId ?? this.sessionId,
2300
+ topK: options?.limit ?? this.options.contextLimit,
2301
+ clientName: "whisper-wrapper"
2302
+ });
2303
+ const prepared = await runtime.beforeTurn({
2304
+ userMessage: query
2305
+ });
2306
+ const results = prepared.items.map((item, index) => ({
2307
+ id: item.id || `runtime_${index}`,
2308
+ content: item.content,
2309
+ score: item.score,
2310
+ metadata: item.metadata || {},
2311
+ source: item.type === "memory" ? "memory" : "runtime",
2312
+ document: item.sourceQuery,
2313
+ type: item.type,
2314
+ retrieval_source: item.type === "memory" ? "memory" : "runtime"
2315
+ }));
2316
+ const context = results.map((r, i) => `[${i + 1}] ${r.content}`).join("\n");
2317
+ return {
2318
+ context: context ? `${this.options.contextPrefix}
2319
+ ${context}` : "",
2320
+ results,
2321
+ count: prepared.items.length
2322
+ };
2323
+ }
2324
+ /**
2325
+ * Remember what happened AFTER your LLM response
2326
+ *
2327
+ * Fire-and-forget - doesn't block your response
2328
+ *
2329
+ * @param content - What your LLM responded with
2330
+ * @returns Promise that resolves when stored (or fails silently)
2331
+ *
2332
+ * @example
2333
+ * ```typescript
2334
+ * const llmResponse = "I've set your theme to dark mode and removed nuts from recommendations.";
2335
+ *
2336
+ * await whisper.remember(llmResponse, { userId: "user-123" });
2337
+ * // → Auto-extracts: "theme set to dark mode", "nut allergy"
2338
+ * // → Stored as preferences
2339
+ * ```
2340
+ */
2341
+ async remember(content, options) {
2342
+ if (!content || content.length < 5) {
2343
+ return { success: false };
2344
+ }
2345
+ try {
2346
+ if (this.options.autoExtract) {
2347
+ const extraction = await this.client.extractMemories({
2348
+ project: options?.project ?? this.options.project,
2349
+ message: content,
2350
+ user_id: options?.userId ?? this.userId,
2351
+ session_id: options?.sessionId ?? this.sessionId,
2352
+ enable_pattern: true,
2353
+ enable_inference: true,
2354
+ min_confidence: this.options.autoExtractMinConfidence
2355
+ });
2356
+ const extractedMemories = (extraction.all || []).filter((m) => (m.confidence || 0) >= this.options.autoExtractMinConfidence).slice(0, this.options.maxMemoriesPerCapture);
2357
+ if (extractedMemories.length > 0) {
2358
+ const bulk = await this.client.addMemoriesBulk({
2359
+ project: options?.project ?? this.options.project,
2360
+ write_mode: "async",
2361
+ memories: extractedMemories.map((m) => ({
2362
+ content: m.content,
2363
+ memory_type: m.memoryType,
2364
+ user_id: options?.userId ?? this.userId,
2365
+ session_id: options?.sessionId ?? this.sessionId,
2366
+ importance: Math.max(0.5, Math.min(1, m.confidence || 0.7)),
2367
+ confidence: m.confidence || 0.7,
2368
+ entity_mentions: m.entityMentions || [],
2369
+ event_date: m.eventDate || void 0,
2370
+ metadata: {
2371
+ extracted: true,
2372
+ extraction_method: extraction.extractionMethod,
2373
+ extraction_reasoning: m.reasoning,
2374
+ inferred: Boolean(m.inferred)
2375
+ }
2376
+ }))
2377
+ });
2378
+ const memoryIds = this.extractMemoryIdsFromBulkResponse(bulk);
2379
+ return {
2380
+ success: true,
2381
+ memoryId: memoryIds[0],
2382
+ memoryIds: memoryIds.length > 0 ? memoryIds : void 0,
2383
+ extracted: extractedMemories.length
2384
+ };
2385
+ }
2386
+ }
2387
+ const result = await this.client.addMemory({
2388
+ project: options?.project ?? this.options.project,
2389
+ content,
2390
+ user_id: options?.userId ?? this.userId,
2391
+ session_id: options?.sessionId ?? this.sessionId
2392
+ });
2393
+ return {
2394
+ success: true,
2395
+ memoryId: result?.id
2396
+ };
2397
+ } catch (error) {
2398
+ console.error("[Whisper] Remember failed:", error);
2399
+ return { success: false };
2400
+ }
2337
2401
  }
2338
- async refreshProjectCache(force = false) {
2339
- if (!force && Date.now() < this.projectCacheExpiresAt && this.projectCache.length > 0) {
2340
- return this.projectCache;
2341
- }
2342
- const response = await this.runtimeClient.request({
2343
- endpoint: "/v1/projects",
2344
- method: "GET",
2345
- operation: "get",
2346
- idempotent: true
2347
- });
2348
- this.projectRefToId.clear();
2349
- this.projectCache = response.data?.projects || [];
2350
- for (const project of this.projectCache) {
2351
- this.projectRefToId.set(project.id, project.id);
2352
- this.projectRefToId.set(project.slug, project.id);
2353
- this.projectRefToId.set(project.name, project.id);
2354
- }
2355
- this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS;
2356
- return this.projectCache;
2402
+ /**
2403
+ * Alias for remember() - same thing
2404
+ */
2405
+ async capture(content, options) {
2406
+ return this.remember(content, options);
2357
2407
  }
2358
- async fetchResolvedProject(projectRef) {
2408
+ /**
2409
+ * Capture from multiple messages (e.g., full conversation)
2410
+ */
2411
+ async captureSession(messages, options) {
2359
2412
  try {
2360
- const response = await this.runtimeClient.request({
2361
- endpoint: `/v1/projects/resolve?project=${encodeURIComponent(projectRef)}`,
2362
- method: "GET",
2363
- operation: "get",
2364
- idempotent: true
2413
+ const filteredMessages = messages.filter((m) => m.role !== "system");
2414
+ const runtime = this.runtimeClient.createAgentRuntime({
2415
+ project: options?.project ?? this.options.project,
2416
+ userId: options?.userId ?? this.userId,
2417
+ sessionId: options?.sessionId ?? this.sessionId ?? "default",
2418
+ clientName: "whisper-wrapper"
2365
2419
  });
2366
- return response.data?.resolved || null;
2420
+ const result = await runtime.afterTurn({
2421
+ userMessage: [...filteredMessages].reverse().find((m) => m.role === "user")?.content || "",
2422
+ assistantMessage: [...filteredMessages].reverse().find((m) => m.role === "assistant")?.content || ""
2423
+ });
2424
+ return {
2425
+ success: true,
2426
+ extracted: result.memoriesCreated ?? 0
2427
+ };
2367
2428
  } catch (error) {
2368
- if (error instanceof RuntimeClientError && error.status === 404) {
2369
- return null;
2429
+ const fallback = await this.fallbackCaptureViaAddMemory(messages, options);
2430
+ if (fallback.success) {
2431
+ return fallback;
2370
2432
  }
2371
- throw error;
2433
+ console.error("[Whisper] Session capture failed:", error);
2434
+ return { success: false, extracted: 0 };
2372
2435
  }
2373
2436
  }
2374
- async resolveProject(projectRef) {
2375
- const resolvedRef = this.getRequiredProject(projectRef);
2376
- const cachedProjects = await this.refreshProjectCache(false);
2377
- const cachedProject = cachedProjects.find(
2378
- (project) => project.id === resolvedRef || project.slug === resolvedRef || project.name === resolvedRef
2379
- );
2380
- if (cachedProject) {
2381
- return cachedProject;
2382
- }
2383
- const resolvedProject = await this.fetchResolvedProject(resolvedRef);
2384
- if (resolvedProject) {
2385
- this.projectRefToId.set(resolvedProject.id, resolvedProject.id);
2386
- this.projectRefToId.set(resolvedProject.slug, resolvedProject.id);
2387
- this.projectRefToId.set(resolvedProject.name, resolvedProject.id);
2388
- this.projectCache = [
2389
- ...this.projectCache.filter((project) => project.id !== resolvedProject.id),
2390
- resolvedProject
2391
- ];
2392
- this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS;
2393
- return resolvedProject;
2394
- }
2395
- if (isLikelyProjectId(resolvedRef)) {
2396
- return {
2397
- id: resolvedRef,
2398
- orgId: "",
2399
- name: resolvedRef,
2400
- slug: resolvedRef,
2401
- createdAt: (/* @__PURE__ */ new Date(0)).toISOString(),
2402
- updatedAt: (/* @__PURE__ */ new Date(0)).toISOString()
2403
- };
2404
- }
2405
- throw new RuntimeClientError({
2406
- code: "PROJECT_NOT_FOUND",
2407
- message: `Project '${resolvedRef}' not found`,
2408
- retryable: false
2437
+ /**
2438
+ * Run a full agent turn with automatic memory read (before) + write (after).
2439
+ */
2440
+ async runTurn(params) {
2441
+ const contextResult = await this.getContext(params.userMessage, {
2442
+ userId: params.userId,
2443
+ sessionId: params.sessionId,
2444
+ project: params.project,
2445
+ limit: params.limit
2409
2446
  });
2447
+ const prompt = contextResult.context ? `${contextResult.context}
2448
+
2449
+ User: ${params.userMessage}` : params.userMessage;
2450
+ const response = await params.generate(prompt);
2451
+ const captureResult = await this.captureSession(
2452
+ [
2453
+ { role: "user", content: params.userMessage },
2454
+ { role: "assistant", content: response }
2455
+ ],
2456
+ {
2457
+ userId: params.userId,
2458
+ sessionId: params.sessionId,
2459
+ project: params.project
2460
+ }
2461
+ );
2462
+ return {
2463
+ response,
2464
+ context: contextResult.context,
2465
+ count: contextResult.count,
2466
+ extracted: captureResult.extracted
2467
+ };
2410
2468
  }
2411
- async query(params) {
2412
- const project = (await this.resolveProject(params.project)).id;
2413
- const response = await this.runtimeClient.request({
2414
- endpoint: "/v1/context/query",
2415
- method: "POST",
2416
- operation: "search",
2417
- body: {
2418
- ...params,
2419
- project
2420
- },
2421
- idempotent: true
2422
- });
2423
- return response.data;
2469
+ /**
2470
+ * Direct access to WhisperContext for advanced usage
2471
+ */
2472
+ raw() {
2473
+ return this.client;
2424
2474
  }
2425
- async ingestSession(params) {
2426
- const project = (await this.resolveProject(params.project)).id;
2427
- const response = await this.runtimeClient.request({
2428
- endpoint: "/v1/memory/ingest/session",
2429
- method: "POST",
2430
- operation: "session",
2431
- body: {
2432
- ...params,
2433
- project
2475
+ extractMemoryIdsFromBulkResponse(bulkResponse) {
2476
+ const ids = [];
2477
+ if (Array.isArray(bulkResponse?.memories)) {
2478
+ for (const memory of bulkResponse.memories) {
2479
+ if (memory?.id) ids.push(memory.id);
2434
2480
  }
2435
- });
2436
- return response.data;
2481
+ }
2482
+ if (bulkResponse?.memory?.id) {
2483
+ ids.push(bulkResponse.memory.id);
2484
+ }
2485
+ if (bulkResponse?.id) {
2486
+ ids.push(bulkResponse.id);
2487
+ }
2488
+ return Array.from(new Set(ids));
2437
2489
  }
2438
- createAgentRuntime(options = {}) {
2439
- const baseContext = {
2440
- workspacePath: options.workspacePath,
2441
- project: options.project || this.config.project,
2442
- userId: options.userId,
2443
- sessionId: options.sessionId,
2444
- traceId: options.traceId,
2445
- clientName: options.clientName
2446
- };
2447
- return new WhisperAgentRuntime({
2448
- baseContext,
2449
- options,
2450
- adapter: {
2451
- resolveProject: (project) => this.resolveProject(project),
2452
- query: (params) => this.query(params),
2453
- ingestSession: (params) => this.ingestSession(params),
2454
- getSessionMemories: (params) => this.memory.getSessionMemories(params),
2455
- getUserProfile: (params) => this.memory.getUserProfile(params),
2456
- searchMemories: (params) => this.memory.search(params),
2457
- addMemory: (params) => this.memory.add(params),
2458
- queueStatus: () => this.queue.status(),
2459
- flushQueue: () => this.queue.flush()
2490
+ async fallbackCaptureViaAddMemory(messages, options) {
2491
+ const userMessages = messages.filter((m) => m.role === "user").map((m) => (m.content || "").trim()).filter((content) => content.length >= 5).slice(-2);
2492
+ if (userMessages.length === 0) {
2493
+ return { success: false, extracted: 0 };
2494
+ }
2495
+ let extracted = 0;
2496
+ for (const content of userMessages) {
2497
+ try {
2498
+ await this.client.addMemory({
2499
+ project: options?.project ?? this.options.project,
2500
+ content,
2501
+ memory_type: "factual",
2502
+ user_id: options?.userId ?? this.userId,
2503
+ session_id: options?.sessionId ?? this.sessionId,
2504
+ allow_legacy_fallback: true
2505
+ });
2506
+ extracted += 1;
2507
+ } catch {
2460
2508
  }
2461
- });
2462
- }
2463
- withRunContext(context) {
2464
- const base = this;
2465
- return {
2466
- memory: {
2467
- add: (params) => base.memory.add({
2468
- ...params,
2469
- project: params.project || context.project || base.config.project,
2470
- user_id: params.user_id || context.userId,
2471
- session_id: params.session_id || context.sessionId
2472
- }),
2473
- search: (params) => base.memory.search({
2474
- ...params,
2475
- project: params.project || context.project || base.config.project,
2476
- user_id: params.user_id || context.userId,
2477
- session_id: params.session_id || context.sessionId
2478
- })
2479
- },
2480
- session: {
2481
- event: (params) => base.session.event({
2482
- ...params,
2483
- sessionId: params.sessionId || context.sessionId || ""
2484
- })
2485
- },
2486
- queue: base.queue,
2487
- diagnostics: base.diagnostics
2488
- };
2489
- }
2490
- async shutdown() {
2491
- await this.writeQueue.stop();
2509
+ }
2510
+ return { success: extracted > 0, extracted };
2492
2511
  }
2493
2512
  };
2494
- var whisper_default = WhisperClient;
2513
+ var whisper_agent_default = Whisper;
2495
2514
 
2496
2515
  // ../src/sdk/middleware.ts
2497
2516
  var WhisperAgentMiddleware = class {