@usewhisper/sdk 3.4.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.
Files changed (5) hide show
  1. package/index.d.mts +249 -1
  2. package/index.d.ts +249 -1
  3. package/index.js +1304 -541
  4. package/index.mjs +1304 -541
  5. package/package.json +1 -1
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
  };
@@ -1599,204 +1314,1252 @@ var AnalyticsModule = class {
1599
1314
  }
1600
1315
  };
1601
1316
 
1602
- // ../src/sdk/whisper.ts
1603
- var WhisperClient = class _WhisperClient {
1604
- constructor(config) {
1605
- this.config = config;
1606
- this.diagnosticsStore = new DiagnosticsStore(config.telemetry?.maxEntries || 1e3);
1607
- this.runtimeClient = new RuntimeClient(
1608
- {
1609
- apiKey: config.apiKey,
1610
- baseUrl: config.baseUrl,
1611
- compatMode: config.compatMode || "fallback",
1612
- timeouts: config.timeouts,
1613
- retryPolicy: config.retryPolicy,
1614
- fetchImpl: config.fetch
1317
+ // ../src/sdk/agent-runtime.ts
1318
+ function detectBrowserStorage() {
1319
+ const maybeStorage = globalThis.localStorage;
1320
+ if (!maybeStorage || typeof maybeStorage !== "object") return null;
1321
+ const candidate = maybeStorage;
1322
+ if (typeof candidate.getItem !== "function" || typeof candidate.setItem !== "function") {
1323
+ return null;
1324
+ }
1325
+ return {
1326
+ getItem: candidate.getItem,
1327
+ setItem: candidate.setItem
1328
+ };
1329
+ }
1330
+ function createBindingStore(filePath) {
1331
+ const storage = detectBrowserStorage();
1332
+ if (storage) {
1333
+ const key = "whisper_agent_runtime_bindings";
1334
+ return {
1335
+ async load() {
1336
+ const raw = storage.getItem(key);
1337
+ if (!raw) return {};
1338
+ try {
1339
+ const parsed = JSON.parse(raw);
1340
+ return parsed && typeof parsed === "object" ? parsed : {};
1341
+ } catch {
1342
+ return {};
1343
+ }
1615
1344
  },
1616
- this.diagnosticsStore
1617
- );
1618
- this.searchCache = new SearchResponseCache(
1619
- config.cache?.ttlMs ?? 7e3,
1620
- config.cache?.capacity ?? 500
1345
+ async save(bindings) {
1346
+ storage.setItem(key, JSON.stringify(bindings));
1347
+ }
1348
+ };
1349
+ }
1350
+ return {
1351
+ async load() {
1352
+ if (typeof process === "undefined") return {};
1353
+ const fs = await import("fs/promises");
1354
+ const path = filePath || `${process.env.USERPROFILE || process.env.HOME || "."}/.whisper/sdk/agent-bindings.json`;
1355
+ try {
1356
+ const raw = await fs.readFile(path, "utf8");
1357
+ const parsed = JSON.parse(raw);
1358
+ return parsed && typeof parsed === "object" ? parsed : {};
1359
+ } catch {
1360
+ return {};
1361
+ }
1362
+ },
1363
+ async save(bindings) {
1364
+ if (typeof process === "undefined") return;
1365
+ const fs = await import("fs/promises");
1366
+ const pathMod = await import("path");
1367
+ const path = filePath || `${process.env.USERPROFILE || process.env.HOME || "."}/.whisper/sdk/agent-bindings.json`;
1368
+ await fs.mkdir(pathMod.dirname(path), { recursive: true });
1369
+ await fs.writeFile(path, JSON.stringify(bindings), "utf8");
1370
+ }
1371
+ };
1372
+ }
1373
+ function normalizeWorkspacePath(value) {
1374
+ const trimmed = value?.trim();
1375
+ if (!trimmed) return null;
1376
+ return trimmed.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
1377
+ }
1378
+ function pathBase(value) {
1379
+ const normalized = value.replace(/\\/g, "/");
1380
+ const parts = normalized.split("/").filter(Boolean);
1381
+ return parts[parts.length - 1] || normalized;
1382
+ }
1383
+ function defaultSalience(kind, success) {
1384
+ if (kind === "decision" || kind === "constraint" || kind === "failure") return "high";
1385
+ if (kind === "outcome" || kind === "task_update") return "medium";
1386
+ if (kind === "tool_result" && success === false) return "high";
1387
+ return "low";
1388
+ }
1389
+ function toMemoryType(kind) {
1390
+ if (kind === "decision" || kind === "constraint") return "instruction";
1391
+ return "event";
1392
+ }
1393
+ function summarizeLowSalience(events) {
1394
+ const lines = events.slice(-10).map((event) => {
1395
+ const fileSuffix = event.filePaths?.length ? ` [files: ${event.filePaths.join(", ")}]` : "";
1396
+ const toolSuffix = event.toolName ? ` [tool: ${event.toolName}]` : "";
1397
+ return `- ${event.kind}: ${event.summary}${fileSuffix}${toolSuffix}`;
1398
+ });
1399
+ return `Recent low-salience work:
1400
+ ${lines.join("\n")}`;
1401
+ }
1402
+ function compactWhitespace(value) {
1403
+ return value.replace(/\s+/g, " ").trim();
1404
+ }
1405
+ function withTimeout(promise, timeoutMs) {
1406
+ return new Promise((resolve, reject) => {
1407
+ const timeout = setTimeout(() => {
1408
+ reject(new Error("timeout"));
1409
+ }, timeoutMs);
1410
+ promise.then(
1411
+ (value) => {
1412
+ clearTimeout(timeout);
1413
+ resolve(value);
1414
+ },
1415
+ (error) => {
1416
+ clearTimeout(timeout);
1417
+ reject(error);
1418
+ }
1621
1419
  );
1622
- const queueStore = config.queue?.persistence === "storage" ? createStorageQueueStore() : config.queue?.persistence === "file" && config.queue.filePath ? createFileQueueStore(config.queue.filePath) : new InMemoryQueueStore();
1623
- this.writeQueue = new WriteQueue({
1624
- store: queueStore,
1625
- maxBatchSize: config.queue?.maxBatchSize ?? 50,
1626
- flushIntervalMs: config.queue?.flushIntervalMs ?? 100,
1627
- maxAttempts: config.queue?.maxAttempts ?? 2,
1628
- flushHandler: async (items) => {
1629
- if (items.length === 0) return;
1630
- const project = items[0].project;
1631
- const memories = items.map((item) => ({
1632
- ...item.payload,
1633
- user_id: item.payload.user_id ?? item.userId,
1634
- session_id: item.payload.session_id ?? item.sessionId,
1635
- metadata: {
1636
- ...item.payload.metadata || {},
1637
- event_id: item.eventId,
1638
- queued_at: item.createdAt
1639
- }
1640
- }));
1641
- try {
1642
- await this.runtimeClient.request({
1643
- endpoint: "/v1/memory/bulk",
1644
- method: "POST",
1645
- operation: "bulk",
1646
- body: {
1647
- project,
1648
- write_mode: "async",
1649
- memories
1650
- }
1651
- });
1652
- } catch (error) {
1653
- if (this.runtimeClient.getCompatMode() !== "fallback" || !(error instanceof RuntimeClientError) || error.status !== 404) {
1654
- throw error;
1655
- }
1656
- await Promise.all(
1657
- memories.map(async (memory) => {
1658
- try {
1659
- await this.runtimeClient.request({
1660
- endpoint: "/v1/memory",
1661
- method: "POST",
1662
- operation: "writeAck",
1663
- body: {
1664
- project,
1665
- ...memory,
1666
- write_mode: "sync"
1667
- }
1668
- });
1669
- } catch (fallbackError) {
1670
- if (this.runtimeClient.getCompatMode() !== "fallback" || !(fallbackError instanceof RuntimeClientError) || fallbackError.status !== 404) {
1671
- throw fallbackError;
1672
- }
1673
- await this.runtimeClient.request({
1674
- endpoint: "/v1/memories",
1675
- method: "POST",
1676
- operation: "writeAck",
1677
- body: {
1678
- project,
1420
+ });
1421
+ }
1422
+ function extractTimestamp(metadata) {
1423
+ const candidates = [
1424
+ metadata?.updatedAt,
1425
+ metadata?.createdAt,
1426
+ metadata?.timestamp,
1427
+ metadata?.event_date,
1428
+ metadata?.eventDate
1429
+ ];
1430
+ for (const value of candidates) {
1431
+ if (typeof value === "string") {
1432
+ const parsed = Date.parse(value);
1433
+ if (!Number.isNaN(parsed)) return parsed;
1434
+ }
1435
+ }
1436
+ return 0;
1437
+ }
1438
+ function salienceBoost(metadata) {
1439
+ const value = metadata?.salience;
1440
+ if (value === "high") return 0.12;
1441
+ if (value === "medium") return 0.06;
1442
+ return 0;
1443
+ }
1444
+ var WhisperAgentRuntime = class {
1445
+ constructor(args) {
1446
+ this.args = args;
1447
+ this.bindingStore = createBindingStore(args.options.bindingStorePath);
1448
+ this.topK = args.options.topK ?? 6;
1449
+ this.maxTokens = args.options.maxTokens ?? 4e3;
1450
+ this.targetRetrievalMs = args.options.targetRetrievalMs ?? 2500;
1451
+ this.hardRetrievalTimeoutMs = args.options.hardRetrievalTimeoutMs ?? 4e3;
1452
+ this.recentWorkLimit = args.options.recentWorkLimit ?? 40;
1453
+ this.baseContext = args.baseContext;
1454
+ this.clientName = args.baseContext.clientName || "whisper-agent-runtime";
1455
+ }
1456
+ bindingStore;
1457
+ topK;
1458
+ maxTokens;
1459
+ targetRetrievalMs;
1460
+ hardRetrievalTimeoutMs;
1461
+ recentWorkLimit;
1462
+ baseContext;
1463
+ clientName;
1464
+ bindings = null;
1465
+ touchedFiles = [];
1466
+ recentWork = [];
1467
+ bufferedLowSalience = [];
1468
+ lastPreparedTurn = null;
1469
+ mergedCount = 0;
1470
+ droppedCount = 0;
1471
+ lastScope = {};
1472
+ async getBindings() {
1473
+ if (!this.bindings) {
1474
+ this.bindings = await this.bindingStore.load();
1475
+ }
1476
+ return this.bindings;
1477
+ }
1478
+ pushTouchedFiles(paths) {
1479
+ if (!paths || paths.length === 0) return;
1480
+ for (const path of paths) {
1481
+ if (!path) continue;
1482
+ this.touchedFiles = [...this.touchedFiles.filter((entry) => entry !== path), path].slice(-20);
1483
+ }
1484
+ }
1485
+ pushWorkEvent(event) {
1486
+ this.recentWork = [...this.recentWork, event].slice(-this.recentWorkLimit);
1487
+ }
1488
+ makeTaskFrameQuery(input) {
1489
+ const task = compactWhitespace(input.taskSummary || "");
1490
+ const salient = this.recentWork.filter((event) => event.salience === "high").slice(-3).map((event) => `${event.kind}: ${event.summary}`);
1491
+ const files = [...input.touchedFiles || [], ...this.touchedFiles].slice(-3).map((file) => pathBase(file));
1492
+ const parts = [
1493
+ task ? `task ${task}` : "",
1494
+ salient.length > 0 ? `recent ${salient.join(" ; ")}` : "",
1495
+ files.length > 0 ? `files ${files.join(" ")}` : "",
1496
+ input.toolContext ? `tool context ${compactWhitespace(input.toolContext)}` : ""
1497
+ ].filter(Boolean);
1498
+ if (parts.length === 0) return null;
1499
+ return parts.join(" | ");
1500
+ }
1501
+ async resolveScope(overrides) {
1502
+ const merged = {
1503
+ ...this.baseContext,
1504
+ ...overrides
1505
+ };
1506
+ const normalizedWorkspace = normalizeWorkspacePath(merged.workspacePath);
1507
+ const bindings = await this.getBindings();
1508
+ const workspaceProject = normalizedWorkspace ? bindings[normalizedWorkspace] : void 0;
1509
+ const configuredProject = merged.project;
1510
+ let projectRef = configuredProject;
1511
+ let projectSource = overrides?.project ? "explicit" : "generated";
1512
+ let warning;
1513
+ if (workspaceProject) {
1514
+ projectRef = workspaceProject;
1515
+ projectSource = "workspace";
1516
+ if (configuredProject && workspaceProject !== configuredProject) {
1517
+ warning = `workspace mapping '${workspaceProject}' overrides configured project '${configuredProject}'`;
1518
+ }
1519
+ } else if (configuredProject) {
1520
+ projectRef = configuredProject;
1521
+ projectSource = overrides?.project ? "explicit" : "config";
1522
+ }
1523
+ const project = (await this.args.adapter.resolveProject(projectRef)).id;
1524
+ if (normalizedWorkspace) {
1525
+ bindings[normalizedWorkspace] = project;
1526
+ await this.bindingStore.save(bindings);
1527
+ }
1528
+ const scope = {
1529
+ ...merged,
1530
+ project,
1531
+ userId: merged.userId || `${this.clientName}-user`,
1532
+ sessionId: merged.sessionId || `sess_${stableHash(`${this.clientName}_${normalizedWorkspace || "default"}`)}`
1533
+ };
1534
+ this.lastScope = {
1535
+ project: scope.project,
1536
+ userId: scope.userId,
1537
+ sessionId: scope.sessionId,
1538
+ source: projectSource,
1539
+ warning
1540
+ };
1541
+ return { scope, projectSource, warning };
1542
+ }
1543
+ async runBranch(name, task) {
1544
+ const startedAt = Date.now();
1545
+ try {
1546
+ const value = await withTimeout(task(), this.hardRetrievalTimeoutMs);
1547
+ return {
1548
+ name,
1549
+ status: "ok",
1550
+ durationMs: Date.now() - startedAt,
1551
+ value
1552
+ };
1553
+ } catch (error) {
1554
+ const durationMs = Date.now() - startedAt;
1555
+ const reason = error instanceof Error ? error.message : String(error);
1556
+ return {
1557
+ name,
1558
+ status: reason === "timeout" ? "timeout" : "error",
1559
+ durationMs,
1560
+ reason
1561
+ };
1562
+ }
1563
+ }
1564
+ contextItems(result, sourceQuery) {
1565
+ return (result.results || []).map((item) => ({
1566
+ id: item.id,
1567
+ content: item.content,
1568
+ type: "project",
1569
+ score: item.score ?? 0,
1570
+ sourceQuery,
1571
+ metadata: item.metadata || {}
1572
+ }));
1573
+ }
1574
+ memoryItems(result, sourceQuery) {
1575
+ return (result.results || []).map((item, index) => ({
1576
+ id: item.memory?.id || item.chunk?.id || `${sourceQuery}_memory_${index}`,
1577
+ content: item.chunk?.content || item.memory?.content || "",
1578
+ type: "memory",
1579
+ score: item.similarity ?? 0,
1580
+ sourceQuery,
1581
+ metadata: {
1582
+ ...item.chunk?.metadata || {},
1583
+ ...item.memory?.temporal || {},
1584
+ confidence: item.memory?.confidence
1585
+ }
1586
+ })).filter((item) => item.content);
1587
+ }
1588
+ rerank(items) {
1589
+ const deduped = /* @__PURE__ */ new Map();
1590
+ for (const item of items) {
1591
+ const key = `${item.id}:${item.content.toLowerCase()}`;
1592
+ const recency = extractTimestamp(item.metadata) > 0 ? 0.04 : 0;
1593
+ const queryBonus = item.sourceQuery === "primary" ? 0.08 : item.sourceQuery === "task_frame" ? 0.04 : 0.03;
1594
+ const next = {
1595
+ ...item,
1596
+ score: item.score + queryBonus + salienceBoost(item.metadata) + recency
1597
+ };
1598
+ const existing = deduped.get(key);
1599
+ if (!existing || next.score > existing.score) {
1600
+ deduped.set(key, next);
1601
+ }
1602
+ }
1603
+ return [...deduped.values()].sort((left, right) => right.score - left.score);
1604
+ }
1605
+ buildContext(items) {
1606
+ const maxChars = this.maxTokens * 4;
1607
+ const lines = [];
1608
+ let used = 0;
1609
+ for (const item of items) {
1610
+ const label = item.type === "memory" ? "memory" : "context";
1611
+ const content = compactWhitespace(item.content);
1612
+ if (!content) continue;
1613
+ const line = `[${label}] ${content}`;
1614
+ if (used + line.length > maxChars) break;
1615
+ lines.push(line);
1616
+ used += line.length;
1617
+ }
1618
+ if (lines.length === 0) return "";
1619
+ return `Relevant context:
1620
+ ${lines.join("\n")}`;
1621
+ }
1622
+ async bootstrap(context = {}) {
1623
+ const { scope, warning } = await this.resolveScope(context);
1624
+ const warnings = warning ? [warning] : [];
1625
+ const startedAt = Date.now();
1626
+ const branches = await Promise.all([
1627
+ this.runBranch("session_recent", () => this.args.adapter.getSessionMemories({
1628
+ project: scope.project,
1629
+ session_id: scope.sessionId,
1630
+ include_pending: true,
1631
+ limit: 12
1632
+ })),
1633
+ this.runBranch("user_profile", () => scope.userId ? this.args.adapter.getUserProfile({
1634
+ project: scope.project,
1635
+ user_id: scope.userId,
1636
+ include_pending: true,
1637
+ memory_types: "preference,instruction,goal"
1638
+ }) : Promise.resolve({ user_id: scope.userId, memories: [], count: 0 })),
1639
+ this.runBranch("project_rules", () => this.args.adapter.query({
1640
+ project: scope.project,
1641
+ query: "project rules instructions constraints conventions open threads",
1642
+ top_k: this.topK,
1643
+ include_memories: false,
1644
+ user_id: scope.userId,
1645
+ session_id: scope.sessionId,
1646
+ max_tokens: this.maxTokens,
1647
+ compress: true,
1648
+ compression_strategy: "adaptive"
1649
+ }))
1650
+ ]);
1651
+ const items = [];
1652
+ const branchStatus = {};
1653
+ for (const branch of branches) {
1654
+ branchStatus[branch.name] = branch.status;
1655
+ if (branch.status !== "ok") {
1656
+ if (branch.reason) warnings.push(`${branch.name}:${branch.reason}`);
1657
+ continue;
1658
+ }
1659
+ if (branch.name === "project_rules") {
1660
+ items.push(...this.contextItems(branch.value, "bootstrap"));
1661
+ continue;
1662
+ }
1663
+ const records = branch.value.memories || [];
1664
+ items.push(...records.map((memory, index) => ({
1665
+ id: String(memory.id || `${branch.name}_${index}`),
1666
+ content: String(memory.content || ""),
1667
+ type: "memory",
1668
+ score: 0.4,
1669
+ sourceQuery: "bootstrap",
1670
+ metadata: memory
1671
+ })).filter((item) => item.content));
1672
+ }
1673
+ const ranked = this.rerank(items).slice(0, this.topK * 2);
1674
+ const prepared = {
1675
+ scope,
1676
+ retrieval: {
1677
+ primaryQuery: "bootstrap",
1678
+ taskFrameQuery: null,
1679
+ warnings,
1680
+ degraded: warnings.length > 0,
1681
+ degradedReason: warnings.length > 0 ? "partial_bootstrap" : void 0,
1682
+ durationMs: Date.now() - startedAt,
1683
+ targetBudgetMs: this.targetRetrievalMs,
1684
+ hardTimeoutMs: this.hardRetrievalTimeoutMs,
1685
+ branchStatus
1686
+ },
1687
+ context: this.buildContext(ranked),
1688
+ items: ranked
1689
+ };
1690
+ this.lastPreparedTurn = prepared.retrieval;
1691
+ return prepared;
1692
+ }
1693
+ async beforeTurn(input, context = {}) {
1694
+ this.pushTouchedFiles(input.touchedFiles);
1695
+ const { scope, warning } = await this.resolveScope(context);
1696
+ const primaryQuery = compactWhitespace(input.userMessage);
1697
+ const taskFrameQuery = this.makeTaskFrameQuery(input);
1698
+ const warnings = warning ? [warning] : [];
1699
+ const startedAt = Date.now();
1700
+ const branches = await Promise.all([
1701
+ this.runBranch("context_primary", () => this.args.adapter.query({
1702
+ project: scope.project,
1703
+ query: primaryQuery,
1704
+ top_k: this.topK,
1705
+ include_memories: false,
1706
+ user_id: scope.userId,
1707
+ session_id: scope.sessionId,
1708
+ max_tokens: this.maxTokens,
1709
+ compress: true,
1710
+ compression_strategy: "adaptive"
1711
+ })),
1712
+ this.runBranch("memory_primary", () => this.args.adapter.searchMemories({
1713
+ project: scope.project,
1714
+ query: primaryQuery,
1715
+ user_id: scope.userId,
1716
+ session_id: scope.sessionId,
1717
+ top_k: this.topK,
1718
+ include_pending: true,
1719
+ profile: "balanced"
1720
+ })),
1721
+ taskFrameQuery ? this.runBranch("context_task_frame", () => this.args.adapter.query({
1722
+ project: scope.project,
1723
+ query: taskFrameQuery,
1724
+ top_k: this.topK,
1725
+ include_memories: false,
1726
+ user_id: scope.userId,
1727
+ session_id: scope.sessionId,
1728
+ max_tokens: this.maxTokens,
1729
+ compress: true,
1730
+ compression_strategy: "adaptive"
1731
+ })) : Promise.resolve({
1732
+ name: "context_task_frame",
1733
+ status: "skipped",
1734
+ durationMs: 0
1735
+ }),
1736
+ taskFrameQuery ? this.runBranch("memory_task_frame", () => this.args.adapter.searchMemories({
1737
+ project: scope.project,
1738
+ query: taskFrameQuery,
1739
+ user_id: scope.userId,
1740
+ session_id: scope.sessionId,
1741
+ top_k: this.topK,
1742
+ include_pending: true,
1743
+ profile: "balanced"
1744
+ })) : Promise.resolve({
1745
+ name: "memory_task_frame",
1746
+ status: "skipped",
1747
+ durationMs: 0
1748
+ })
1749
+ ]);
1750
+ const branchStatus = {};
1751
+ const collected = [];
1752
+ let okCount = 0;
1753
+ for (const branch of branches) {
1754
+ branchStatus[branch.name] = branch.status;
1755
+ if (branch.status !== "ok") {
1756
+ if (branch.status !== "skipped" && branch.reason) warnings.push(`${branch.name}:${branch.reason}`);
1757
+ continue;
1758
+ }
1759
+ okCount += 1;
1760
+ if (branch.name.startsWith("context")) {
1761
+ collected.push(...this.contextItems(
1762
+ branch.value,
1763
+ branch.name.includes("task_frame") ? "task_frame" : "primary"
1764
+ ));
1765
+ } else {
1766
+ collected.push(...this.memoryItems(
1767
+ branch.value,
1768
+ branch.name.includes("task_frame") ? "task_frame" : "primary"
1769
+ ));
1770
+ }
1771
+ }
1772
+ const ranked = this.rerank(collected).slice(0, this.topK * 2);
1773
+ const prepared = {
1774
+ scope,
1775
+ retrieval: {
1776
+ primaryQuery,
1777
+ taskFrameQuery,
1778
+ warnings,
1779
+ degraded: okCount < branches.filter((branch) => branch.status !== "skipped").length,
1780
+ degradedReason: okCount === 0 ? "all_retrieval_failed" : warnings.length > 0 ? "partial_retrieval_failed" : void 0,
1781
+ durationMs: Date.now() - startedAt,
1782
+ targetBudgetMs: this.targetRetrievalMs,
1783
+ hardTimeoutMs: this.hardRetrievalTimeoutMs,
1784
+ branchStatus
1785
+ },
1786
+ context: this.buildContext(ranked),
1787
+ items: ranked
1788
+ };
1789
+ this.lastPreparedTurn = prepared.retrieval;
1790
+ return prepared;
1791
+ }
1792
+ async recordWork(event, context = {}) {
1793
+ const normalized = {
1794
+ ...event,
1795
+ salience: event.salience || defaultSalience(event.kind, event.success),
1796
+ timestamp: event.timestamp || nowIso()
1797
+ };
1798
+ this.pushTouchedFiles(normalized.filePaths);
1799
+ this.pushWorkEvent(normalized);
1800
+ if (normalized.salience === "low") {
1801
+ this.bufferedLowSalience = [...this.bufferedLowSalience, normalized].slice(-20);
1802
+ return { success: true, buffered: true };
1803
+ }
1804
+ const { scope } = await this.resolveScope(context);
1805
+ return this.args.adapter.addMemory({
1806
+ project: scope.project,
1807
+ user_id: scope.userId,
1808
+ session_id: scope.sessionId,
1809
+ content: `${normalized.kind}: ${normalized.summary}${normalized.details ? ` (${normalized.details})` : ""}`,
1810
+ memory_type: toMemoryType(normalized.kind),
1811
+ event_date: normalized.timestamp,
1812
+ write_mode: "async",
1813
+ metadata: {
1814
+ runtime_auto: true,
1815
+ client_name: this.clientName,
1816
+ work_event_kind: normalized.kind,
1817
+ salience: normalized.salience,
1818
+ file_paths: normalized.filePaths || [],
1819
+ tool_name: normalized.toolName,
1820
+ success: normalized.success
1821
+ }
1822
+ });
1823
+ }
1824
+ async afterTurn(input, context = {}) {
1825
+ this.pushTouchedFiles(input.touchedFiles);
1826
+ const { scope } = await this.resolveScope(context);
1827
+ const result = await this.args.adapter.ingestSession({
1828
+ project: scope.project,
1829
+ session_id: scope.sessionId,
1830
+ user_id: scope.userId,
1831
+ messages: [
1832
+ { role: "user", content: input.userMessage, timestamp: nowIso() },
1833
+ { role: "assistant", content: input.assistantMessage, timestamp: nowIso() }
1834
+ ],
1835
+ write_mode: "async"
1836
+ });
1837
+ this.mergedCount += result.memories_invalidated || 0;
1838
+ return {
1839
+ success: Boolean(result.success),
1840
+ sessionIngested: true,
1841
+ memoriesCreated: result.memories_created || 0,
1842
+ relationsCreated: result.relations_created || 0,
1843
+ invalidatedCount: result.memories_invalidated || 0,
1844
+ mergedCount: result.memories_invalidated || 0,
1845
+ droppedCount: 0,
1846
+ warnings: result.errors || []
1847
+ };
1848
+ }
1849
+ async flush(reason = "manual", context = {}) {
1850
+ if (this.bufferedLowSalience.length > 0) {
1851
+ const { scope } = await this.resolveScope(context);
1852
+ const summary = summarizeLowSalience(this.bufferedLowSalience);
1853
+ await this.args.adapter.addMemory({
1854
+ project: scope.project,
1855
+ user_id: scope.userId,
1856
+ session_id: scope.sessionId,
1857
+ content: summary,
1858
+ memory_type: "event",
1859
+ write_mode: "async",
1860
+ metadata: {
1861
+ runtime_auto: true,
1862
+ client_name: this.clientName,
1863
+ flush_reason: reason,
1864
+ salience: "low",
1865
+ summarized_count: this.bufferedLowSalience.length
1866
+ }
1867
+ });
1868
+ this.bufferedLowSalience = [];
1869
+ }
1870
+ await this.args.adapter.flushQueue();
1871
+ return this.status();
1872
+ }
1873
+ status() {
1874
+ return {
1875
+ clientName: this.clientName,
1876
+ scope: this.lastScope,
1877
+ queue: this.args.adapter.queueStatus(),
1878
+ retrieval: this.lastPreparedTurn,
1879
+ counters: {
1880
+ mergedCount: this.mergedCount,
1881
+ droppedCount: this.droppedCount,
1882
+ bufferedLowSalience: this.bufferedLowSalience.length
1883
+ }
1884
+ };
1885
+ }
1886
+ };
1887
+
1888
+ // ../src/sdk/whisper.ts
1889
+ var PROJECT_CACHE_TTL_MS = 3e4;
1890
+ function isLikelyProjectId(projectRef) {
1891
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(projectRef);
1892
+ }
1893
+ var WhisperClient = class _WhisperClient {
1894
+ constructor(config) {
1895
+ this.config = config;
1896
+ this.diagnosticsStore = new DiagnosticsStore(config.telemetry?.maxEntries || 1e3);
1897
+ this.runtimeClient = new RuntimeClient(
1898
+ {
1899
+ apiKey: config.apiKey,
1900
+ baseUrl: config.baseUrl,
1901
+ compatMode: config.compatMode || "fallback",
1902
+ timeouts: config.timeouts,
1903
+ retryPolicy: config.retryPolicy,
1904
+ fetchImpl: config.fetch
1905
+ },
1906
+ this.diagnosticsStore
1907
+ );
1908
+ this.searchCache = new SearchResponseCache(
1909
+ config.cache?.ttlMs ?? 7e3,
1910
+ config.cache?.capacity ?? 500
1911
+ );
1912
+ const queueStore = this.createQueueStore(config);
1913
+ this.writeQueue = new WriteQueue({
1914
+ store: queueStore,
1915
+ maxBatchSize: config.queue?.maxBatchSize ?? 50,
1916
+ flushIntervalMs: config.queue?.flushIntervalMs ?? 100,
1917
+ maxAttempts: config.queue?.maxAttempts ?? 2,
1918
+ flushHandler: async (items) => {
1919
+ if (items.length === 0) return;
1920
+ const project = items[0].project;
1921
+ const memories = items.map((item) => ({
1922
+ ...item.payload,
1923
+ user_id: item.payload.user_id ?? item.userId,
1924
+ session_id: item.payload.session_id ?? item.sessionId,
1925
+ metadata: {
1926
+ ...item.payload.metadata || {},
1927
+ event_id: item.eventId,
1928
+ queued_at: item.createdAt
1929
+ }
1930
+ }));
1931
+ try {
1932
+ await this.runtimeClient.request({
1933
+ endpoint: "/v1/memory/bulk",
1934
+ method: "POST",
1935
+ operation: "bulk",
1936
+ body: {
1937
+ project,
1938
+ write_mode: "async",
1939
+ memories
1940
+ }
1941
+ });
1942
+ } catch (error) {
1943
+ if (this.runtimeClient.getCompatMode() !== "fallback" || !(error instanceof RuntimeClientError) || error.status !== 404) {
1944
+ throw error;
1945
+ }
1946
+ await Promise.all(
1947
+ memories.map(async (memory) => {
1948
+ try {
1949
+ await this.runtimeClient.request({
1950
+ endpoint: "/v1/memory",
1951
+ method: "POST",
1952
+ operation: "writeAck",
1953
+ body: {
1954
+ project,
1955
+ ...memory,
1956
+ write_mode: "sync"
1957
+ }
1958
+ });
1959
+ } catch (fallbackError) {
1960
+ if (this.runtimeClient.getCompatMode() !== "fallback" || !(fallbackError instanceof RuntimeClientError) || fallbackError.status !== 404) {
1961
+ throw fallbackError;
1962
+ }
1963
+ await this.runtimeClient.request({
1964
+ endpoint: "/v1/memories",
1965
+ method: "POST",
1966
+ operation: "writeAck",
1967
+ body: {
1968
+ project,
1679
1969
  ...memory,
1680
1970
  memory_type: memory.memory_type === "event" ? "episodic" : memory.memory_type
1681
1971
  }
1682
1972
  });
1683
1973
  }
1684
- })
1685
- );
1974
+ })
1975
+ );
1976
+ }
1977
+ }
1978
+ });
1979
+ if (config.queue?.enabled !== false) {
1980
+ void this.writeQueue.start();
1981
+ }
1982
+ this.memoryModule = new MemoryModule(
1983
+ this.runtimeClient,
1984
+ this.searchCache,
1985
+ this.writeQueue,
1986
+ {
1987
+ defaultProject: config.project,
1988
+ cacheEnabled: config.cache?.enabled !== false,
1989
+ queueEnabled: config.queue?.enabled !== false
1990
+ }
1991
+ );
1992
+ this.sessionModule = new SessionModule(this.memoryModule, config.project);
1993
+ this.profileModule = new ProfileModule(this.memoryModule);
1994
+ this.analyticsModule = new AnalyticsModule(this.diagnosticsStore, this.writeQueue);
1995
+ this.diagnostics = {
1996
+ getLast: (limit) => this.diagnosticsStore.getLast(limit),
1997
+ subscribe: (fn) => this.diagnosticsStore.subscribe(fn),
1998
+ snapshot: () => this.diagnosticsStore.snapshot()
1999
+ };
2000
+ this.queue = {
2001
+ flush: () => this.writeQueue.flush(),
2002
+ status: () => this.writeQueue.status()
2003
+ };
2004
+ this.memory = {
2005
+ add: (params) => this.memoryModule.add(params),
2006
+ addBulk: (params) => this.memoryModule.addBulk(params),
2007
+ search: (params) => this.memoryModule.search(params),
2008
+ get: (memoryId) => this.memoryModule.get(memoryId),
2009
+ getUserProfile: (params) => this.memoryModule.getUserProfile(params),
2010
+ getSessionMemories: (params) => this.memoryModule.getSessionMemories(params),
2011
+ update: (memoryId, params) => this.memoryModule.update(memoryId, params),
2012
+ delete: (memoryId) => this.memoryModule.delete(memoryId),
2013
+ flag: (params) => this.memoryModule.flag(params)
2014
+ };
2015
+ this.session = {
2016
+ start: (params) => this.sessionModule.start(params),
2017
+ event: (params) => this.sessionModule.event(params),
2018
+ suspend: (params) => this.sessionModule.suspend(params),
2019
+ resume: (params) => this.sessionModule.resume(params),
2020
+ end: (params) => this.sessionModule.end(params)
2021
+ };
2022
+ this.profile = {
2023
+ getUserProfile: (params) => this.profileModule.getUserProfile(params),
2024
+ getSessionMemories: (params) => this.profileModule.getSessionMemories(params)
2025
+ };
2026
+ this.analytics = {
2027
+ diagnosticsSnapshot: () => this.analyticsModule.diagnosticsSnapshot(),
2028
+ queueStatus: () => this.analyticsModule.queueStatus()
2029
+ };
2030
+ }
2031
+ diagnostics;
2032
+ queue;
2033
+ memory;
2034
+ session;
2035
+ profile;
2036
+ analytics;
2037
+ runtimeClient;
2038
+ diagnosticsStore;
2039
+ searchCache;
2040
+ writeQueue;
2041
+ memoryModule;
2042
+ sessionModule;
2043
+ profileModule;
2044
+ analyticsModule;
2045
+ projectRefToId = /* @__PURE__ */ new Map();
2046
+ projectCache = [];
2047
+ projectCacheExpiresAt = 0;
2048
+ static fromEnv(overrides = {}) {
2049
+ const env = typeof process !== "undefined" ? process.env : {};
2050
+ const apiKey = overrides.apiKey || env.WHISPER_API_KEY || env.USEWHISPER_API_KEY || env.API_KEY;
2051
+ if (!apiKey) {
2052
+ throw new Error("Missing API key. Set WHISPER_API_KEY / USEWHISPER_API_KEY / API_KEY.");
2053
+ }
2054
+ return new _WhisperClient({
2055
+ apiKey,
2056
+ baseUrl: overrides.baseUrl || env.WHISPER_BASE_URL || env.API_BASE_URL || "https://context.usewhisper.dev",
2057
+ project: overrides.project || env.WHISPER_PROJECT || env.PROJECT,
2058
+ ...overrides
2059
+ });
2060
+ }
2061
+ createQueueStore(config) {
2062
+ const persistence = config.queue?.persistence || this.defaultQueuePersistence();
2063
+ if (persistence === "storage") {
2064
+ return createStorageQueueStore();
2065
+ }
2066
+ if (persistence === "file") {
2067
+ const filePath = config.queue?.filePath || this.defaultQueueFilePath();
2068
+ if (filePath) {
2069
+ return createFileQueueStore(filePath);
2070
+ }
2071
+ }
2072
+ return new InMemoryQueueStore();
2073
+ }
2074
+ defaultQueuePersistence() {
2075
+ const maybeWindow = globalThis.window;
2076
+ if (maybeWindow && typeof maybeWindow === "object") {
2077
+ const maybeStorage = globalThis.localStorage;
2078
+ return maybeStorage && typeof maybeStorage === "object" ? "storage" : "memory";
2079
+ }
2080
+ return "file";
2081
+ }
2082
+ defaultQueueFilePath() {
2083
+ if (typeof process === "undefined") return void 0;
2084
+ const path = process.env.WHISPER_QUEUE_FILE_PATH;
2085
+ if (path) return path;
2086
+ const home = process.env.USERPROFILE || process.env.HOME;
2087
+ if (!home) return void 0;
2088
+ const normalizedHome = home.replace(/[\\\/]+$/, "");
2089
+ return `${normalizedHome}/.whisper/sdk/queue.json`;
2090
+ }
2091
+ getRequiredProject(project) {
2092
+ const resolved = project || this.config.project;
2093
+ if (!resolved) {
2094
+ throw new RuntimeClientError({
2095
+ code: "MISSING_PROJECT",
2096
+ message: "Project is required",
2097
+ retryable: false
2098
+ });
2099
+ }
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
+ };
1686
2434
  }
1687
2435
  }
1688
- });
1689
- if (config.queue?.enabled !== false) {
1690
- void this.writeQueue.start();
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 };
1691
2449
  }
1692
- this.memoryModule = new MemoryModule(
1693
- this.runtimeClient,
1694
- this.searchCache,
1695
- this.writeQueue,
2450
+ }
2451
+ /**
2452
+ * Alias for remember() - same thing
2453
+ */
2454
+ async capture(content, options) {
2455
+ return this.remember(content, options);
2456
+ }
2457
+ /**
2458
+ * Capture from multiple messages (e.g., full conversation)
2459
+ */
2460
+ async captureSession(messages, options) {
2461
+ try {
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"
2468
+ });
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
+ };
2477
+ } catch (error) {
2478
+ const fallback = await this.fallbackCaptureViaAddMemory(messages, options);
2479
+ if (fallback.success) {
2480
+ return fallback;
2481
+ }
2482
+ console.error("[Whisper] Session capture failed:", error);
2483
+ return { success: false, extracted: 0 };
2484
+ }
2485
+ }
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
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
+ ],
1696
2505
  {
1697
- defaultProject: config.project,
1698
- cacheEnabled: config.cache?.enabled !== false,
1699
- queueEnabled: config.queue?.enabled !== false
2506
+ userId: params.userId,
2507
+ sessionId: params.sessionId,
2508
+ project: params.project
1700
2509
  }
1701
2510
  );
1702
- this.sessionModule = new SessionModule(this.memoryModule, config.project);
1703
- this.profileModule = new ProfileModule(this.memoryModule);
1704
- this.analyticsModule = new AnalyticsModule(this.diagnosticsStore, this.writeQueue);
1705
- this.diagnostics = {
1706
- getLast: (limit) => this.diagnosticsStore.getLast(limit),
1707
- subscribe: (fn) => this.diagnosticsStore.subscribe(fn),
1708
- snapshot: () => this.diagnosticsStore.snapshot()
1709
- };
1710
- this.queue = {
1711
- flush: () => this.writeQueue.flush(),
1712
- status: () => this.writeQueue.status()
1713
- };
1714
- this.memory = {
1715
- add: (params) => this.memoryModule.add(params),
1716
- addBulk: (params) => this.memoryModule.addBulk(params),
1717
- search: (params) => this.memoryModule.search(params),
1718
- get: (memoryId) => this.memoryModule.get(memoryId),
1719
- getUserProfile: (params) => this.memoryModule.getUserProfile(params),
1720
- getSessionMemories: (params) => this.memoryModule.getSessionMemories(params),
1721
- update: (memoryId, params) => this.memoryModule.update(memoryId, params),
1722
- delete: (memoryId) => this.memoryModule.delete(memoryId),
1723
- flag: (params) => this.memoryModule.flag(params)
1724
- };
1725
- this.session = {
1726
- start: (params) => this.sessionModule.start(params),
1727
- event: (params) => this.sessionModule.event(params),
1728
- suspend: (params) => this.sessionModule.suspend(params),
1729
- resume: (params) => this.sessionModule.resume(params),
1730
- end: (params) => this.sessionModule.end(params)
1731
- };
1732
- this.profile = {
1733
- getUserProfile: (params) => this.profileModule.getUserProfile(params),
1734
- getSessionMemories: (params) => this.profileModule.getSessionMemories(params)
1735
- };
1736
- this.analytics = {
1737
- diagnosticsSnapshot: () => this.analyticsModule.diagnosticsSnapshot(),
1738
- queueStatus: () => this.analyticsModule.queueStatus()
2511
+ return {
2512
+ response,
2513
+ context: contextResult.context,
2514
+ count: contextResult.count,
2515
+ extracted: captureResult.extracted
1739
2516
  };
1740
2517
  }
1741
- diagnostics;
1742
- queue;
1743
- memory;
1744
- session;
1745
- profile;
1746
- analytics;
1747
- runtimeClient;
1748
- diagnosticsStore;
1749
- searchCache;
1750
- writeQueue;
1751
- memoryModule;
1752
- sessionModule;
1753
- profileModule;
1754
- analyticsModule;
1755
- static fromEnv(overrides = {}) {
1756
- const env = typeof process !== "undefined" ? process.env : {};
1757
- const apiKey = overrides.apiKey || env.WHISPER_API_KEY || env.USEWHISPER_API_KEY || env.API_KEY;
1758
- if (!apiKey) {
1759
- throw new Error("Missing API key. Set WHISPER_API_KEY / USEWHISPER_API_KEY / API_KEY.");
1760
- }
1761
- return new _WhisperClient({
1762
- apiKey,
1763
- baseUrl: overrides.baseUrl || env.WHISPER_BASE_URL || env.API_BASE_URL || "https://context.usewhisper.dev",
1764
- project: overrides.project || env.WHISPER_PROJECT || env.PROJECT,
1765
- ...overrides
1766
- });
2518
+ /**
2519
+ * Direct access to WhisperContext for advanced usage
2520
+ */
2521
+ raw() {
2522
+ return this.client;
1767
2523
  }
1768
- withRunContext(context) {
1769
- const base = this;
1770
- return {
1771
- memory: {
1772
- add: (params) => base.memory.add({
1773
- ...params,
1774
- project: params.project || context.project || base.config.project,
1775
- user_id: params.user_id || context.userId,
1776
- session_id: params.session_id || context.sessionId
1777
- }),
1778
- search: (params) => base.memory.search({
1779
- ...params,
1780
- project: params.project || context.project || base.config.project,
1781
- user_id: params.user_id || context.userId,
1782
- session_id: params.session_id || context.sessionId
1783
- })
1784
- },
1785
- session: {
1786
- event: (params) => base.session.event({
1787
- ...params,
1788
- sessionId: params.sessionId || context.sessionId || ""
1789
- })
1790
- },
1791
- queue: base.queue,
1792
- diagnostics: base.diagnostics
1793
- };
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);
2529
+ }
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));
1794
2538
  }
1795
- async shutdown() {
1796
- await this.writeQueue.stop();
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 {
2557
+ }
2558
+ }
2559
+ return { success: extracted > 0, extracted };
1797
2560
  }
1798
2561
  };
1799
- var whisper_default = WhisperClient;
2562
+ var whisper_agent_default = Whisper;
1800
2563
 
1801
2564
  // ../src/sdk/middleware.ts
1802
2565
  var WhisperAgentMiddleware = class {
@@ -2306,7 +3069,7 @@ var DEFAULT_MAX_ATTEMPTS = 3;
2306
3069
  var DEFAULT_BASE_DELAY_MS = 250;
2307
3070
  var DEFAULT_MAX_DELAY_MS = 2e3;
2308
3071
  var DEFAULT_TIMEOUT_MS = 15e3;
2309
- var PROJECT_CACHE_TTL_MS = 3e4;
3072
+ var PROJECT_CACHE_TTL_MS2 = 3e4;
2310
3073
  var DEPRECATION_WARNINGS2 = /* @__PURE__ */ new Set();
2311
3074
  function warnDeprecatedOnce2(key, message) {
2312
3075
  if (DEPRECATION_WARNINGS2.has(key)) return;
@@ -2315,7 +3078,7 @@ function warnDeprecatedOnce2(key, message) {
2315
3078
  console.warn(message);
2316
3079
  }
2317
3080
  }
2318
- function isLikelyProjectId(projectRef) {
3081
+ function isLikelyProjectId2(projectRef) {
2319
3082
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(projectRef);
2320
3083
  }
2321
3084
  function normalizeBaseUrl2(url) {
@@ -2437,7 +3200,7 @@ var WhisperContext = class _WhisperContext {
2437
3200
  }
2438
3201
  }
2439
3202
  }
2440
- this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS;
3203
+ this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS2;
2441
3204
  return this.projectCache;
2442
3205
  }
2443
3206
  async fetchResolvedProject(projectRef) {
@@ -2470,10 +3233,10 @@ var WhisperContext = class _WhisperContext {
2470
3233
  ...this.projectCache.filter((project) => project.id !== resolvedProject.id),
2471
3234
  resolvedProject
2472
3235
  ];
2473
- this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS;
3236
+ this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS2;
2474
3237
  return resolvedProject;
2475
3238
  }
2476
- if (isLikelyProjectId(resolvedRef)) {
3239
+ if (isLikelyProjectId2(resolvedRef)) {
2477
3240
  return {
2478
3241
  id: resolvedRef,
2479
3242
  orgId: "",
@@ -2505,7 +3268,7 @@ var WhisperContext = class _WhisperContext {
2505
3268
  message: `Project reference '${projectRef}' matched multiple projects. Use project id instead.`
2506
3269
  });
2507
3270
  }
2508
- if (isLikelyProjectId(projectRef)) {
3271
+ if (isLikelyProjectId2(projectRef)) {
2509
3272
  return projectRef;
2510
3273
  }
2511
3274
  const resolvedProject = await this.fetchResolvedProject(projectRef);
@@ -2517,7 +3280,7 @@ var WhisperContext = class _WhisperContext {
2517
3280
  ...this.projectCache.filter((project) => project.id !== resolvedProject.id),
2518
3281
  resolvedProject
2519
3282
  ];
2520
- this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS;
3283
+ this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS2;
2521
3284
  return resolvedProject.id;
2522
3285
  }
2523
3286
  throw new WhisperError({
@@ -2534,7 +3297,7 @@ var WhisperContext = class _WhisperContext {
2534
3297
  candidates.add(match.id);
2535
3298
  candidates.add(match.slug);
2536
3299
  candidates.add(match.name);
2537
- } else if (isLikelyProjectId(projectRef)) {
3300
+ } else if (isLikelyProjectId2(projectRef)) {
2538
3301
  const byId = projects.find((p) => p.id === projectRef);
2539
3302
  if (byId) {
2540
3303
  candidates.add(byId.slug);
@@ -2695,7 +3458,7 @@ var WhisperContext = class _WhisperContext {
2695
3458
  ...this.projectCache.filter((p) => p.id !== project.id),
2696
3459
  project
2697
3460
  ];
2698
- this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS;
3461
+ this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS2;
2699
3462
  return project;
2700
3463
  }
2701
3464
  async listProjects() {
@@ -2706,7 +3469,7 @@ var WhisperContext = class _WhisperContext {
2706
3469
  this.projectRefToId.set(p.slug, p.id);
2707
3470
  this.projectRefToId.set(p.name, p.id);
2708
3471
  }
2709
- this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS;
3472
+ this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS2;
2710
3473
  return projects;
2711
3474
  }
2712
3475
  async getProject(id) {