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