@usewhisper/sdk 2.2.1 → 3.0.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 +648 -5
  2. package/index.d.ts +648 -5
  3. package/index.js +2020 -156
  4. package/index.mjs +2004 -156
  5. package/package.json +1 -1
package/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,23 +17,410 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // ../src/sdk/index.ts
21
31
  var index_exports = {};
22
32
  __export(index_exports, {
33
+ LangChainMemoryAdapter: () => LangChainMemoryAdapter,
34
+ LangGraphCheckpointAdapter: () => LangGraphCheckpointAdapter,
23
35
  Whisper: () => Whisper,
24
36
  WhisperAgentMiddleware: () => WhisperAgentMiddleware,
37
+ WhisperClient: () => WhisperClient,
25
38
  WhisperContext: () => WhisperContext,
26
39
  WhisperDefault: () => whisper_agent_default,
27
40
  WhisperError: () => WhisperError,
41
+ WhisperRuntimeClient: () => whisper_default,
28
42
  createAgentMiddleware: () => createAgentMiddleware,
43
+ createLangChainMemoryAdapter: () => createLangChainMemoryAdapter,
44
+ createLangGraphCheckpointAdapter: () => createLangGraphCheckpointAdapter,
29
45
  default: () => index_default,
30
46
  memoryGraphToMermaid: () => memoryGraphToMermaid
31
47
  });
32
48
  module.exports = __toCommonJS(index_exports);
33
49
 
50
+ // ../src/sdk/core/telemetry.ts
51
+ var DiagnosticsStore = class {
52
+ maxEntries;
53
+ records = [];
54
+ subscribers = /* @__PURE__ */ new Set();
55
+ constructor(maxEntries = 1e3) {
56
+ this.maxEntries = Math.max(1, maxEntries);
57
+ }
58
+ add(record) {
59
+ this.records.push(record);
60
+ if (this.records.length > this.maxEntries) {
61
+ this.records.splice(0, this.records.length - this.maxEntries);
62
+ }
63
+ for (const fn of this.subscribers) {
64
+ try {
65
+ fn(record);
66
+ } catch {
67
+ }
68
+ }
69
+ }
70
+ getLast(limit = 25) {
71
+ const count = Math.max(1, limit);
72
+ return this.records.slice(-count);
73
+ }
74
+ snapshot() {
75
+ const total = this.records.length;
76
+ const success = this.records.filter((r) => r.success).length;
77
+ const failure = total - success;
78
+ const duration = this.records.reduce((acc, item) => acc + item.durationMs, 0);
79
+ return {
80
+ total,
81
+ success,
82
+ failure,
83
+ avgDurationMs: total > 0 ? duration / total : 0,
84
+ lastTraceId: this.records[this.records.length - 1]?.traceId
85
+ };
86
+ }
87
+ subscribe(fn) {
88
+ this.subscribers.add(fn);
89
+ return () => {
90
+ this.subscribers.delete(fn);
91
+ };
92
+ }
93
+ };
94
+
95
+ // ../src/sdk/core/utils.ts
96
+ function normalizeBaseUrl(url) {
97
+ let normalized = url.trim().replace(/\/+$/, "");
98
+ normalized = normalized.replace(/\/api\/v1$/i, "");
99
+ normalized = normalized.replace(/\/v1$/i, "");
100
+ normalized = normalized.replace(/\/api$/i, "");
101
+ return normalized;
102
+ }
103
+ function normalizeEndpoint(endpoint) {
104
+ const withLeadingSlash = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
105
+ if (/^\/api\/v1(\/|$)/i.test(withLeadingSlash)) {
106
+ return withLeadingSlash.replace(/^\/api/i, "");
107
+ }
108
+ return withLeadingSlash;
109
+ }
110
+ function nowIso() {
111
+ return (/* @__PURE__ */ new Date()).toISOString();
112
+ }
113
+ function stableHash(input) {
114
+ let hash = 2166136261;
115
+ for (let i = 0; i < input.length; i += 1) {
116
+ hash ^= input.charCodeAt(i);
117
+ hash = Math.imul(hash, 16777619);
118
+ }
119
+ return (hash >>> 0).toString(16).padStart(8, "0");
120
+ }
121
+ function normalizeQuery(query) {
122
+ return query.trim().toLowerCase().replace(/\s+/g, " ");
123
+ }
124
+ function randomId(prefix = "id") {
125
+ return `${prefix}_${stableHash(`${Date.now()}_${Math.random()}`)}`;
126
+ }
127
+
128
+ // ../src/sdk/core/client.ts
129
+ var DEFAULT_TIMEOUTS = {
130
+ searchMs: 3e3,
131
+ writeAckMs: 2e3,
132
+ bulkMs: 1e4,
133
+ profileMs: 2500,
134
+ sessionMs: 2500
135
+ };
136
+ var DEFAULT_RETRYABLE_STATUS = [408, 429, 500, 502, 503, 504];
137
+ var DEFAULT_RETRY_ATTEMPTS = {
138
+ search: 3,
139
+ writeAck: 2,
140
+ bulk: 2,
141
+ profile: 2,
142
+ session: 2,
143
+ query: 3,
144
+ get: 2
145
+ };
146
+ function isObject(value) {
147
+ return typeof value === "object" && value !== null;
148
+ }
149
+ function toMessage(payload, status, statusText) {
150
+ if (typeof payload === "string" && payload.trim()) return payload;
151
+ if (isObject(payload)) {
152
+ const maybeError = payload.error;
153
+ const maybeMessage = payload.message;
154
+ if (typeof maybeError === "string" && maybeError.trim()) return maybeError;
155
+ if (typeof maybeMessage === "string" && maybeMessage.trim()) return maybeMessage;
156
+ if (isObject(maybeError) && typeof maybeError.message === "string") return maybeError.message;
157
+ }
158
+ return `HTTP ${status}: ${statusText}`;
159
+ }
160
+ var RuntimeClientError = class extends Error {
161
+ status;
162
+ retryable;
163
+ code;
164
+ details;
165
+ traceId;
166
+ constructor(args) {
167
+ super(args.message);
168
+ this.name = "RuntimeClientError";
169
+ this.status = args.status;
170
+ this.retryable = args.retryable;
171
+ this.code = args.code;
172
+ this.details = args.details;
173
+ this.traceId = args.traceId;
174
+ }
175
+ };
176
+ var RuntimeClient = class {
177
+ apiKey;
178
+ baseUrl;
179
+ sdkVersion;
180
+ compatMode;
181
+ retryPolicy;
182
+ timeouts;
183
+ diagnostics;
184
+ inFlight = /* @__PURE__ */ new Map();
185
+ constructor(options, diagnostics) {
186
+ if (!options.apiKey) {
187
+ throw new RuntimeClientError({
188
+ code: "INVALID_API_KEY",
189
+ message: "API key is required",
190
+ retryable: false
191
+ });
192
+ }
193
+ this.apiKey = options.apiKey;
194
+ this.baseUrl = normalizeBaseUrl(options.baseUrl || "https://context.usewhisper.dev");
195
+ this.sdkVersion = options.sdkVersion || "2.x-runtime";
196
+ this.compatMode = options.compatMode || "fallback";
197
+ this.retryPolicy = {
198
+ retryableStatusCodes: options.retryPolicy?.retryableStatusCodes || DEFAULT_RETRYABLE_STATUS,
199
+ retryOnNetworkError: options.retryPolicy?.retryOnNetworkError ?? true,
200
+ maxBackoffMs: options.retryPolicy?.maxBackoffMs ?? 1200,
201
+ baseBackoffMs: options.retryPolicy?.baseBackoffMs ?? 250,
202
+ maxAttemptsByOperation: options.retryPolicy?.maxAttemptsByOperation || {}
203
+ };
204
+ this.timeouts = {
205
+ ...DEFAULT_TIMEOUTS,
206
+ ...options.timeouts || {}
207
+ };
208
+ this.diagnostics = diagnostics || new DiagnosticsStore(1e3);
209
+ }
210
+ getDiagnosticsStore() {
211
+ return this.diagnostics;
212
+ }
213
+ getCompatMode() {
214
+ return this.compatMode;
215
+ }
216
+ timeoutFor(operation) {
217
+ switch (operation) {
218
+ case "search":
219
+ return this.timeouts.searchMs;
220
+ case "writeAck":
221
+ return this.timeouts.writeAckMs;
222
+ case "bulk":
223
+ return this.timeouts.bulkMs;
224
+ case "profile":
225
+ return this.timeouts.profileMs;
226
+ case "session":
227
+ return this.timeouts.sessionMs;
228
+ case "query":
229
+ case "get":
230
+ default:
231
+ return this.timeouts.searchMs;
232
+ }
233
+ }
234
+ maxAttemptsFor(operation) {
235
+ const override = this.retryPolicy.maxAttemptsByOperation?.[operation];
236
+ return Math.max(1, override ?? DEFAULT_RETRY_ATTEMPTS[operation]);
237
+ }
238
+ shouldRetryStatus(status) {
239
+ return status !== void 0 && this.retryPolicy.retryableStatusCodes?.includes(status) === true;
240
+ }
241
+ backoff(attempt) {
242
+ const base = this.retryPolicy.baseBackoffMs ?? 250;
243
+ const max = this.retryPolicy.maxBackoffMs ?? 1200;
244
+ const jitter = 0.8 + Math.random() * 0.4;
245
+ return Math.min(max, Math.floor(base * Math.pow(2, attempt) * jitter));
246
+ }
247
+ runtimeName() {
248
+ const maybeWindow = globalThis.window;
249
+ return maybeWindow && typeof maybeWindow === "object" ? "browser" : "node";
250
+ }
251
+ createRequestFingerprint(options) {
252
+ const normalizedEndpoint = normalizeEndpoint(options.endpoint);
253
+ const authFingerprint = stableHash(this.apiKey.replace(/^Bearer\s+/i, ""));
254
+ const payload = JSON.stringify({
255
+ method: options.method || "GET",
256
+ endpoint: normalizedEndpoint,
257
+ body: options.body || null,
258
+ extra: options.dedupeKeyExtra || "",
259
+ authFingerprint
260
+ });
261
+ return stableHash(payload);
262
+ }
263
+ async request(options) {
264
+ const dedupeKey = options.idempotent ? this.createRequestFingerprint(options) : null;
265
+ if (dedupeKey) {
266
+ const inFlight = this.inFlight.get(dedupeKey);
267
+ if (inFlight) {
268
+ const data = await inFlight;
269
+ this.diagnostics.add({
270
+ id: randomId("diag"),
271
+ startedAt: nowIso(),
272
+ endedAt: nowIso(),
273
+ traceId: data.traceId,
274
+ spanId: randomId("span"),
275
+ operation: options.operation,
276
+ method: options.method || "GET",
277
+ endpoint: normalizeEndpoint(options.endpoint),
278
+ status: data.status,
279
+ durationMs: 0,
280
+ success: true,
281
+ deduped: true
282
+ });
283
+ const cloned = {
284
+ data: data.data,
285
+ status: data.status,
286
+ traceId: data.traceId
287
+ };
288
+ return cloned;
289
+ }
290
+ }
291
+ const runner = this.performRequest(options).then((data) => {
292
+ if (dedupeKey) this.inFlight.delete(dedupeKey);
293
+ return data;
294
+ }).catch((error) => {
295
+ if (dedupeKey) this.inFlight.delete(dedupeKey);
296
+ throw error;
297
+ });
298
+ if (dedupeKey) {
299
+ this.inFlight.set(dedupeKey, runner);
300
+ }
301
+ return runner;
302
+ }
303
+ async performRequest(options) {
304
+ const method = options.method || "GET";
305
+ const normalizedEndpoint = normalizeEndpoint(options.endpoint);
306
+ const operation = options.operation;
307
+ const maxAttempts = this.maxAttemptsFor(operation);
308
+ const timeoutMs = this.timeoutFor(operation);
309
+ const traceId = options.traceId || randomId("trace");
310
+ let lastError = null;
311
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
312
+ const spanId = randomId("span");
313
+ const startedAt = Date.now();
314
+ const controller = new AbortController();
315
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
316
+ try {
317
+ const response = await fetch(`${this.baseUrl}${normalizedEndpoint}`, {
318
+ method,
319
+ signal: controller.signal,
320
+ keepalive: method !== "GET",
321
+ headers: {
322
+ "Content-Type": "application/json",
323
+ Authorization: this.apiKey.startsWith("Bearer ") ? this.apiKey : `Bearer ${this.apiKey}`,
324
+ "X-API-Key": this.apiKey.replace(/^Bearer\s+/i, ""),
325
+ "x-trace-id": traceId,
326
+ "x-span-id": spanId,
327
+ "x-sdk-version": this.sdkVersion,
328
+ "x-sdk-runtime": this.runtimeName(),
329
+ ...options.headers || {}
330
+ },
331
+ body: method === "GET" || method === "DELETE" ? void 0 : JSON.stringify(options.body || {})
332
+ });
333
+ clearTimeout(timeout);
334
+ let payload = null;
335
+ try {
336
+ payload = await response.json();
337
+ } catch {
338
+ payload = await response.text().catch(() => "");
339
+ }
340
+ const durationMs = Date.now() - startedAt;
341
+ const record = {
342
+ id: randomId("diag"),
343
+ startedAt: new Date(startedAt).toISOString(),
344
+ endedAt: nowIso(),
345
+ traceId,
346
+ spanId,
347
+ operation,
348
+ method,
349
+ endpoint: normalizedEndpoint,
350
+ status: response.status,
351
+ durationMs,
352
+ success: response.ok
353
+ };
354
+ this.diagnostics.add(record);
355
+ if (response.ok) {
356
+ return {
357
+ data: payload,
358
+ status: response.status,
359
+ traceId
360
+ };
361
+ }
362
+ const message = toMessage(payload, response.status, response.statusText);
363
+ const retryable = this.shouldRetryStatus(response.status);
364
+ const error = new RuntimeClientError({
365
+ message,
366
+ status: response.status,
367
+ retryable,
368
+ code: response.status === 404 ? "NOT_FOUND" : "REQUEST_FAILED",
369
+ details: payload,
370
+ traceId
371
+ });
372
+ lastError = error;
373
+ if (!retryable || attempt === maxAttempts - 1) {
374
+ throw error;
375
+ }
376
+ } catch (error) {
377
+ clearTimeout(timeout);
378
+ const durationMs = Date.now() - startedAt;
379
+ const isAbort = isObject(error) && error.name === "AbortError";
380
+ const mapped = error instanceof RuntimeClientError ? error : new RuntimeClientError({
381
+ message: isAbort ? "Request timed out" : error instanceof Error ? error.message : "Network error",
382
+ retryable: this.retryPolicy.retryOnNetworkError ?? true,
383
+ code: isAbort ? "TIMEOUT" : "NETWORK_ERROR",
384
+ traceId
385
+ });
386
+ lastError = mapped;
387
+ this.diagnostics.add({
388
+ id: randomId("diag"),
389
+ startedAt: new Date(startedAt).toISOString(),
390
+ endedAt: nowIso(),
391
+ traceId,
392
+ spanId,
393
+ operation,
394
+ method,
395
+ endpoint: normalizedEndpoint,
396
+ durationMs,
397
+ success: false,
398
+ errorCode: mapped.code,
399
+ errorMessage: mapped.message
400
+ });
401
+ if (!mapped.retryable || attempt === maxAttempts - 1) {
402
+ throw mapped;
403
+ }
404
+ }
405
+ await new Promise((resolve) => setTimeout(resolve, this.backoff(attempt)));
406
+ }
407
+ throw lastError || new RuntimeClientError({
408
+ message: "Request failed",
409
+ retryable: false,
410
+ code: "REQUEST_FAILED"
411
+ });
412
+ }
413
+ };
414
+
34
415
  // ../src/sdk/whisper-agent.ts
416
+ var DEPRECATION_WARNINGS = /* @__PURE__ */ new Set();
417
+ function warnDeprecatedOnce(key, message) {
418
+ if (DEPRECATION_WARNINGS.has(key)) return;
419
+ DEPRECATION_WARNINGS.add(key);
420
+ if (typeof console !== "undefined" && typeof console.warn === "function") {
421
+ console.warn(message);
422
+ }
423
+ }
35
424
  var Whisper = class {
36
425
  client;
37
426
  options;
@@ -49,6 +438,10 @@ var Whisper = class {
49
438
  if (options.timeoutMs) clientConfig.timeoutMs = options.timeoutMs;
50
439
  if (options.retry) clientConfig.retry = options.retry;
51
440
  this.client = new WhisperContext(clientConfig);
441
+ warnDeprecatedOnce(
442
+ "whisper_agent_wrapper",
443
+ "[Whisper SDK] Whisper wrapper is supported for v2 compatibility. Prefer WhisperClient for new integrations."
444
+ );
52
445
  const finalRetry = options.retry || { maxAttempts: 3, baseDelayMs: 250, maxDelayMs: 2e3 };
53
446
  this.options = {
54
447
  apiKey: options.apiKey,
@@ -213,96 +606,1153 @@ ${context}` : "",
213
606
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
214
607
  }))
215
608
  });
216
- return {
217
- success: true,
218
- extracted: result?.memories_created ?? 0
219
- };
609
+ return {
610
+ success: true,
611
+ extracted: result?.memories_created ?? 0
612
+ };
613
+ } catch (error) {
614
+ const fallback = await this.fallbackCaptureViaAddMemory(messages, options);
615
+ if (fallback.success) {
616
+ return fallback;
617
+ }
618
+ console.error("[Whisper] Session capture failed:", error);
619
+ return { success: false, extracted: 0 };
620
+ }
621
+ }
622
+ /**
623
+ * Run a full agent turn with automatic memory read (before) + write (after).
624
+ */
625
+ async runTurn(params) {
626
+ const contextResult = await this.getContext(params.userMessage, {
627
+ userId: params.userId,
628
+ sessionId: params.sessionId,
629
+ project: params.project,
630
+ limit: params.limit
631
+ });
632
+ const prompt = contextResult.context ? `${contextResult.context}
633
+
634
+ User: ${params.userMessage}` : params.userMessage;
635
+ const response = await params.generate(prompt);
636
+ const captureResult = await this.captureSession(
637
+ [
638
+ { role: "user", content: params.userMessage },
639
+ { role: "assistant", content: response }
640
+ ],
641
+ {
642
+ userId: params.userId,
643
+ sessionId: params.sessionId,
644
+ project: params.project
645
+ }
646
+ );
647
+ return {
648
+ response,
649
+ context: contextResult.context,
650
+ count: contextResult.count,
651
+ extracted: captureResult.extracted
652
+ };
653
+ }
654
+ /**
655
+ * Direct access to WhisperContext for advanced usage
656
+ */
657
+ raw() {
658
+ return this.client;
659
+ }
660
+ extractMemoryIdsFromBulkResponse(bulkResponse) {
661
+ const ids = [];
662
+ if (Array.isArray(bulkResponse?.memories)) {
663
+ for (const memory of bulkResponse.memories) {
664
+ if (memory?.id) ids.push(memory.id);
665
+ }
666
+ }
667
+ if (bulkResponse?.memory?.id) {
668
+ ids.push(bulkResponse.memory.id);
669
+ }
670
+ if (bulkResponse?.id) {
671
+ ids.push(bulkResponse.id);
672
+ }
673
+ return Array.from(new Set(ids));
674
+ }
675
+ async fallbackCaptureViaAddMemory(messages, options) {
676
+ const userMessages = messages.filter((m) => m.role === "user").map((m) => (m.content || "").trim()).filter((content) => content.length >= 5).slice(-2);
677
+ if (userMessages.length === 0) {
678
+ return { success: false, extracted: 0 };
679
+ }
680
+ let extracted = 0;
681
+ for (const content of userMessages) {
682
+ try {
683
+ await this.client.addMemory({
684
+ project: options?.project ?? this.options.project,
685
+ content,
686
+ memory_type: "factual",
687
+ user_id: options?.userId ?? this.userId,
688
+ session_id: options?.sessionId ?? this.sessionId,
689
+ allow_legacy_fallback: true
690
+ });
691
+ extracted += 1;
692
+ } catch {
693
+ }
694
+ }
695
+ return { success: extracted > 0, extracted };
696
+ }
697
+ };
698
+ var whisper_agent_default = Whisper;
699
+
700
+ // ../src/sdk/core/cache.ts
701
+ var SearchResponseCache = class {
702
+ ttlMs;
703
+ capacity;
704
+ byKey = /* @__PURE__ */ new Map();
705
+ scopeIndex = /* @__PURE__ */ new Map();
706
+ constructor(ttlMs = 7e3, capacity = 500) {
707
+ this.ttlMs = Math.max(1e3, ttlMs);
708
+ this.capacity = Math.max(10, capacity);
709
+ }
710
+ makeScopeKey(project, userId, sessionId) {
711
+ return `${project}:${userId || "_"}:${sessionId || "_"}`;
712
+ }
713
+ makeKey(input) {
714
+ const normalized = {
715
+ project: input.project,
716
+ userId: input.userId || "",
717
+ sessionId: input.sessionId || "",
718
+ query: normalizeQuery(input.query),
719
+ topK: input.topK,
720
+ profile: input.profile,
721
+ includePending: input.includePending
722
+ };
723
+ return `search:${stableHash(JSON.stringify(normalized))}`;
724
+ }
725
+ get(key) {
726
+ const found = this.byKey.get(key);
727
+ if (!found) return null;
728
+ if (found.expiresAt <= Date.now()) {
729
+ this.deleteByKey(key);
730
+ return null;
731
+ }
732
+ found.touchedAt = Date.now();
733
+ return found.value;
734
+ }
735
+ set(key, scopeKey, value) {
736
+ this.byKey.set(key, {
737
+ value,
738
+ scopeKey,
739
+ touchedAt: Date.now(),
740
+ expiresAt: Date.now() + this.ttlMs
741
+ });
742
+ if (!this.scopeIndex.has(scopeKey)) {
743
+ this.scopeIndex.set(scopeKey, /* @__PURE__ */ new Set());
744
+ }
745
+ this.scopeIndex.get(scopeKey).add(key);
746
+ this.evictIfNeeded();
747
+ }
748
+ invalidateScope(scopeKey) {
749
+ const keys = this.scopeIndex.get(scopeKey);
750
+ if (!keys || keys.size === 0) {
751
+ return 0;
752
+ }
753
+ const toDelete = Array.from(keys);
754
+ for (const key of toDelete) {
755
+ this.deleteByKey(key);
756
+ }
757
+ this.scopeIndex.delete(scopeKey);
758
+ return toDelete.length;
759
+ }
760
+ evictIfNeeded() {
761
+ if (this.byKey.size <= this.capacity) return;
762
+ const ordered = Array.from(this.byKey.entries()).sort((a, b) => a[1].touchedAt - b[1].touchedAt);
763
+ const removeCount = this.byKey.size - this.capacity;
764
+ for (let i = 0; i < removeCount; i += 1) {
765
+ this.deleteByKey(ordered[i][0]);
766
+ }
767
+ }
768
+ deleteByKey(key) {
769
+ const found = this.byKey.get(key);
770
+ if (!found) return;
771
+ this.byKey.delete(key);
772
+ const scopeKeys = this.scopeIndex.get(found.scopeKey);
773
+ if (!scopeKeys) return;
774
+ scopeKeys.delete(key);
775
+ if (scopeKeys.size === 0) {
776
+ this.scopeIndex.delete(found.scopeKey);
777
+ }
778
+ }
779
+ };
780
+
781
+ // ../src/sdk/core/queue.ts
782
+ var InMemoryQueueStore = class {
783
+ items = [];
784
+ async load() {
785
+ return [...this.items];
786
+ }
787
+ async save(items) {
788
+ this.items = [...items];
789
+ }
790
+ };
791
+ var WriteQueue = class {
792
+ flushHandler;
793
+ store;
794
+ maxBatchSize;
795
+ flushIntervalMs;
796
+ maxAttempts;
797
+ queue = [];
798
+ flushTimer = null;
799
+ flushing = false;
800
+ lastFlushAt;
801
+ lastFlushCount = 0;
802
+ constructor(args) {
803
+ this.flushHandler = args.flushHandler;
804
+ this.store = args.store || new InMemoryQueueStore();
805
+ this.maxBatchSize = Math.max(1, args.maxBatchSize ?? 50);
806
+ this.flushIntervalMs = Math.max(10, args.flushIntervalMs ?? 100);
807
+ this.maxAttempts = Math.max(1, args.maxAttempts ?? 2);
808
+ }
809
+ async start() {
810
+ const pending = await this.store.load();
811
+ if (pending.length > 0) {
812
+ this.queue.push(...pending);
813
+ }
814
+ if (!this.flushTimer) {
815
+ this.flushTimer = setInterval(() => {
816
+ void this.flush();
817
+ }, this.flushIntervalMs);
818
+ const timer = this.flushTimer;
819
+ if (typeof timer.unref === "function") {
820
+ timer.unref();
821
+ }
822
+ }
823
+ this.bindProcessHooks();
824
+ }
825
+ async stop() {
826
+ if (this.flushTimer) {
827
+ clearInterval(this.flushTimer);
828
+ this.flushTimer = null;
829
+ }
830
+ await this.flush();
831
+ }
832
+ status() {
833
+ return {
834
+ queued: this.queue.length,
835
+ flushing: this.flushing,
836
+ lastFlushAt: this.lastFlushAt,
837
+ lastFlushCount: this.lastFlushCount
838
+ };
839
+ }
840
+ async enqueue(input) {
841
+ const eventId = input.eventId || this.makeEventId(input);
842
+ const item = {
843
+ ...input,
844
+ eventId,
845
+ createdAt: nowIso()
846
+ };
847
+ this.queue.push(item);
848
+ await this.store.save(this.queue);
849
+ if (this.queue.length >= this.maxBatchSize) {
850
+ void this.flush();
851
+ }
852
+ return item;
853
+ }
854
+ async flush() {
855
+ if (this.flushing || this.queue.length === 0) return;
856
+ this.flushing = true;
857
+ try {
858
+ while (this.queue.length > 0) {
859
+ const batch = this.queue.slice(0, this.maxBatchSize);
860
+ let done = false;
861
+ let error = null;
862
+ for (let attempt = 0; attempt < this.maxAttempts; attempt += 1) {
863
+ try {
864
+ await this.flushHandler(batch);
865
+ done = true;
866
+ break;
867
+ } catch (err) {
868
+ error = err;
869
+ await new Promise((resolve) => setTimeout(resolve, (attempt + 1) * 180));
870
+ }
871
+ }
872
+ if (!done) {
873
+ throw error instanceof Error ? error : new Error("Queue flush failed");
874
+ }
875
+ this.queue.splice(0, batch.length);
876
+ this.lastFlushAt = nowIso();
877
+ this.lastFlushCount = batch.length;
878
+ await this.store.save(this.queue);
879
+ }
880
+ } finally {
881
+ this.flushing = false;
882
+ }
883
+ }
884
+ makeEventId(input) {
885
+ const source = JSON.stringify({
886
+ project: input.project,
887
+ userId: input.userId || "",
888
+ sessionId: input.sessionId || "",
889
+ payload: input.payload
890
+ });
891
+ return `evt_${stableHash(source)}`;
892
+ }
893
+ bindProcessHooks() {
894
+ if (typeof process === "undefined") return;
895
+ const proc = process;
896
+ const flushOnExit = () => {
897
+ void this.flush();
898
+ };
899
+ proc.once("beforeExit", flushOnExit);
900
+ proc.once("SIGINT", flushOnExit);
901
+ proc.once("SIGTERM", flushOnExit);
902
+ }
903
+ };
904
+ function createStorageQueueStore(key = "whisper_sdk_queue") {
905
+ const getStorage = () => {
906
+ const maybeStorage = globalThis.localStorage;
907
+ if (!maybeStorage || typeof maybeStorage !== "object") return null;
908
+ const candidate = maybeStorage;
909
+ if (typeof candidate.getItem !== "function" || typeof candidate.setItem !== "function") {
910
+ return null;
911
+ }
912
+ return {
913
+ getItem: candidate.getItem,
914
+ setItem: candidate.setItem
915
+ };
916
+ };
917
+ return {
918
+ async load() {
919
+ const storage = getStorage();
920
+ if (!storage) return [];
921
+ const raw = storage.getItem(key);
922
+ if (!raw) return [];
923
+ try {
924
+ const parsed = JSON.parse(raw);
925
+ return Array.isArray(parsed) ? parsed : [];
926
+ } catch {
927
+ return [];
928
+ }
929
+ },
930
+ async save(items) {
931
+ const storage = getStorage();
932
+ if (!storage) return;
933
+ storage.setItem(key, JSON.stringify(items));
934
+ }
935
+ };
936
+ }
937
+ function createFileQueueStore(filePath) {
938
+ return {
939
+ async load() {
940
+ if (typeof process === "undefined") return [];
941
+ const fs = await import("fs/promises");
942
+ try {
943
+ const raw = await fs.readFile(filePath, "utf8");
944
+ const parsed = JSON.parse(raw);
945
+ return Array.isArray(parsed) ? parsed : [];
946
+ } catch (error) {
947
+ const nodeError = error;
948
+ if (nodeError?.code === "ENOENT") {
949
+ return [];
950
+ }
951
+ return [];
952
+ }
953
+ },
954
+ async save(items) {
955
+ if (typeof process === "undefined") return;
956
+ const fs = await import("fs/promises");
957
+ const path = await import("path");
958
+ const dir = path.dirname(filePath);
959
+ await fs.mkdir(dir, { recursive: true });
960
+ await fs.writeFile(filePath, JSON.stringify(items), "utf8");
961
+ }
962
+ };
963
+ }
964
+
965
+ // ../src/sdk/modules/memory.ts
966
+ function isEndpointNotFound(error) {
967
+ return error instanceof RuntimeClientError && error.status === 404;
968
+ }
969
+ function toSotaType(memoryType) {
970
+ if (!memoryType) return void 0;
971
+ switch (memoryType) {
972
+ case "episodic":
973
+ return "event";
974
+ case "semantic":
975
+ return "factual";
976
+ case "procedural":
977
+ return "instruction";
978
+ default:
979
+ return memoryType;
980
+ }
981
+ }
982
+ function toLegacyType(memoryType) {
983
+ if (!memoryType) return void 0;
984
+ switch (memoryType) {
985
+ case "event":
986
+ return "episodic";
987
+ case "instruction":
988
+ return "procedural";
989
+ case "preference":
990
+ case "relationship":
991
+ case "opinion":
992
+ case "goal":
993
+ return "semantic";
994
+ default:
995
+ return memoryType;
996
+ }
997
+ }
998
+ var MemoryModule = class {
999
+ constructor(client, cache, queue, options = {}) {
1000
+ this.client = client;
1001
+ this.cache = cache;
1002
+ this.queue = queue;
1003
+ this.options = options;
1004
+ }
1005
+ resolveProject(project) {
1006
+ const value = project || this.options.defaultProject;
1007
+ if (!value) {
1008
+ throw new RuntimeClientError({
1009
+ code: "MISSING_PROJECT",
1010
+ message: "Project is required",
1011
+ retryable: false
1012
+ });
1013
+ }
1014
+ return value;
1015
+ }
1016
+ invalidate(project, userId, sessionId) {
1017
+ if (this.options.cacheEnabled === false) {
1018
+ return;
1019
+ }
1020
+ const scope = this.cache.makeScopeKey(project, userId, sessionId);
1021
+ this.cache.invalidateScope(scope);
1022
+ }
1023
+ async add(params) {
1024
+ const project = this.resolveProject(params.project);
1025
+ const queueEnabled = this.options.queueEnabled !== false;
1026
+ const useQueue = queueEnabled && params.write_mode !== "sync" && params.async !== false;
1027
+ if (useQueue) {
1028
+ const queued = await this.queue.enqueue({
1029
+ project,
1030
+ userId: params.user_id,
1031
+ sessionId: params.session_id,
1032
+ payload: {
1033
+ content: params.content,
1034
+ memory_type: toSotaType(params.memory_type),
1035
+ user_id: params.user_id,
1036
+ session_id: params.session_id,
1037
+ agent_id: params.agent_id,
1038
+ importance: params.importance,
1039
+ confidence: params.confidence,
1040
+ metadata: params.metadata,
1041
+ document_date: params.document_date,
1042
+ event_date: params.event_date
1043
+ }
1044
+ });
1045
+ this.invalidate(project, params.user_id, params.session_id);
1046
+ return {
1047
+ success: true,
1048
+ mode: "async",
1049
+ queued: true,
1050
+ event_id: queued.eventId,
1051
+ accepted_at: queued.createdAt
1052
+ };
1053
+ }
1054
+ try {
1055
+ const response = await this.client.request({
1056
+ endpoint: "/v1/memory",
1057
+ method: "POST",
1058
+ operation: "writeAck",
1059
+ body: {
1060
+ project,
1061
+ content: params.content,
1062
+ memory_type: toSotaType(params.memory_type),
1063
+ user_id: params.user_id,
1064
+ session_id: params.session_id,
1065
+ agent_id: params.agent_id,
1066
+ importance: params.importance,
1067
+ confidence: params.confidence,
1068
+ metadata: params.metadata,
1069
+ document_date: params.document_date,
1070
+ event_date: params.event_date,
1071
+ write_mode: "sync"
1072
+ }
1073
+ });
1074
+ this.invalidate(project, params.user_id, params.session_id);
1075
+ return {
1076
+ success: true,
1077
+ mode: "sync",
1078
+ trace_id: response.traceId
1079
+ };
1080
+ } catch (error) {
1081
+ if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
1082
+ throw error;
1083
+ }
1084
+ await this.client.request({
1085
+ endpoint: "/v1/memories",
1086
+ method: "POST",
1087
+ operation: "writeAck",
1088
+ body: {
1089
+ project,
1090
+ content: params.content,
1091
+ memory_type: toLegacyType(params.memory_type),
1092
+ user_id: params.user_id,
1093
+ session_id: params.session_id,
1094
+ agent_id: params.agent_id,
1095
+ importance: params.importance,
1096
+ metadata: params.metadata
1097
+ }
1098
+ });
1099
+ this.invalidate(project, params.user_id, params.session_id);
1100
+ return {
1101
+ success: true,
1102
+ mode: "sync"
1103
+ };
1104
+ }
1105
+ }
1106
+ async addBulk(params) {
1107
+ const project = this.resolveProject(params.project);
1108
+ if (!Array.isArray(params.memories) || params.memories.length === 0) {
1109
+ throw new RuntimeClientError({
1110
+ code: "VALIDATION_ERROR",
1111
+ message: "memories is required",
1112
+ retryable: false
1113
+ });
1114
+ }
1115
+ const queueEnabled = this.options.queueEnabled !== false;
1116
+ const useQueue = queueEnabled && params.write_mode !== "sync" && params.async !== false;
1117
+ if (useQueue) {
1118
+ const queued = await Promise.all(
1119
+ params.memories.map(
1120
+ (memory) => this.queue.enqueue({
1121
+ project,
1122
+ userId: memory.user_id,
1123
+ sessionId: memory.session_id,
1124
+ payload: {
1125
+ content: memory.content,
1126
+ memory_type: toSotaType(memory.memory_type),
1127
+ user_id: memory.user_id,
1128
+ session_id: memory.session_id,
1129
+ agent_id: memory.agent_id,
1130
+ importance: memory.importance,
1131
+ confidence: memory.confidence,
1132
+ metadata: memory.metadata,
1133
+ document_date: memory.document_date,
1134
+ event_date: memory.event_date
1135
+ }
1136
+ })
1137
+ )
1138
+ );
1139
+ for (const memory of params.memories) {
1140
+ this.invalidate(project, memory.user_id, memory.session_id);
1141
+ }
1142
+ return {
1143
+ success: true,
1144
+ mode: "async",
1145
+ queued: true,
1146
+ created: queued.length
1147
+ };
1148
+ }
1149
+ try {
1150
+ const response = await this.client.request({
1151
+ endpoint: "/v1/memory/bulk",
1152
+ method: "POST",
1153
+ operation: "bulk",
1154
+ body: {
1155
+ project,
1156
+ memories: params.memories.map((memory) => ({
1157
+ ...memory,
1158
+ memory_type: toSotaType(memory.memory_type)
1159
+ })),
1160
+ write_mode: "sync"
1161
+ }
1162
+ });
1163
+ for (const memory of params.memories) {
1164
+ this.invalidate(project, memory.user_id, memory.session_id);
1165
+ }
1166
+ return {
1167
+ success: true,
1168
+ mode: "sync",
1169
+ trace_id: response.traceId
1170
+ };
1171
+ } catch (error) {
1172
+ if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
1173
+ throw error;
1174
+ }
1175
+ await Promise.all(
1176
+ params.memories.map(
1177
+ (memory) => this.add({
1178
+ project,
1179
+ ...memory,
1180
+ write_mode: "sync"
1181
+ })
1182
+ )
1183
+ );
1184
+ return {
1185
+ success: true,
1186
+ mode: "sync"
1187
+ };
1188
+ }
1189
+ }
1190
+ async search(params) {
1191
+ const project = this.resolveProject(params.project);
1192
+ const topK = params.top_k || 10;
1193
+ const profile = params.profile || "fast";
1194
+ const includePending = params.include_pending !== false;
1195
+ const cacheKey = this.cache.makeKey({
1196
+ project,
1197
+ userId: params.user_id,
1198
+ sessionId: params.session_id,
1199
+ query: params.query,
1200
+ topK,
1201
+ profile,
1202
+ includePending
1203
+ });
1204
+ if (this.options.cacheEnabled !== false) {
1205
+ const cached = this.cache.get(cacheKey);
1206
+ if (cached) {
1207
+ return {
1208
+ ...cached,
1209
+ cache_hit: true
1210
+ };
1211
+ }
1212
+ }
1213
+ try {
1214
+ const response = await this.client.request({
1215
+ endpoint: "/v1/memory/search",
1216
+ method: "POST",
1217
+ operation: "search",
1218
+ idempotent: true,
1219
+ body: {
1220
+ project,
1221
+ query: params.query,
1222
+ user_id: params.user_id,
1223
+ session_id: params.session_id,
1224
+ top_k: topK,
1225
+ profile,
1226
+ include_pending: includePending,
1227
+ memory_types: params.memory_type ? [toSotaType(params.memory_type)] : void 0
1228
+ }
1229
+ });
1230
+ const data = {
1231
+ ...response.data || {},
1232
+ cache_hit: false
1233
+ };
1234
+ if (this.options.cacheEnabled !== false) {
1235
+ const scope = this.cache.makeScopeKey(project, params.user_id, params.session_id);
1236
+ this.cache.set(cacheKey, scope, data);
1237
+ }
1238
+ return data;
1239
+ } catch (error) {
1240
+ if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
1241
+ throw error;
1242
+ }
1243
+ const legacy = await this.client.request({
1244
+ endpoint: "/v1/memories/search",
1245
+ method: "POST",
1246
+ operation: "search",
1247
+ idempotent: true,
1248
+ body: {
1249
+ project,
1250
+ query: params.query,
1251
+ user_id: params.user_id,
1252
+ session_id: params.session_id,
1253
+ top_k: topK,
1254
+ memory_type: toLegacyType(params.memory_type)
1255
+ }
1256
+ });
1257
+ const data = {
1258
+ ...legacy.data || {},
1259
+ cache_hit: false
1260
+ };
1261
+ if (this.options.cacheEnabled !== false) {
1262
+ const scope = this.cache.makeScopeKey(project, params.user_id, params.session_id);
1263
+ this.cache.set(cacheKey, scope, data);
1264
+ }
1265
+ return data;
1266
+ }
1267
+ }
1268
+ async getUserProfile(params) {
1269
+ const project = this.resolveProject(params.project);
1270
+ const query = new URLSearchParams({
1271
+ project,
1272
+ ...params.include_pending !== void 0 ? { include_pending: String(params.include_pending) } : {},
1273
+ ...params.memory_types ? { memory_types: params.memory_types } : {}
1274
+ });
1275
+ try {
1276
+ const response = await this.client.request({
1277
+ endpoint: `/v1/memory/profile/${params.user_id}?${query}`,
1278
+ method: "GET",
1279
+ operation: "profile",
1280
+ idempotent: true
1281
+ });
1282
+ return response.data;
1283
+ } catch (error) {
1284
+ if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
1285
+ throw error;
1286
+ }
1287
+ const legacyQuery = new URLSearchParams({
1288
+ project,
1289
+ user_id: params.user_id,
1290
+ limit: "200"
1291
+ });
1292
+ const legacy = await this.client.request({
1293
+ endpoint: `/v1/memories?${legacyQuery}`,
1294
+ method: "GET",
1295
+ operation: "profile",
1296
+ idempotent: true
1297
+ });
1298
+ const memories = Array.isArray(legacy.data?.memories) ? legacy.data.memories : [];
1299
+ return {
1300
+ user_id: params.user_id,
1301
+ memories,
1302
+ count: memories.length
1303
+ };
1304
+ }
1305
+ }
1306
+ async getSessionMemories(params) {
1307
+ const project = this.resolveProject(params.project);
1308
+ const query = new URLSearchParams({
1309
+ project,
1310
+ ...params.limit ? { limit: String(params.limit) } : {},
1311
+ ...params.include_pending !== void 0 ? { include_pending: String(params.include_pending) } : {}
1312
+ });
1313
+ const response = await this.client.request({
1314
+ endpoint: `/v1/memory/session/${params.session_id}?${query}`,
1315
+ method: "GET",
1316
+ operation: "profile",
1317
+ idempotent: true
1318
+ });
1319
+ return response.data;
1320
+ }
1321
+ async update(memoryId, params) {
1322
+ try {
1323
+ await this.client.request({
1324
+ endpoint: `/v1/memory/${memoryId}`,
1325
+ method: "PUT",
1326
+ operation: "writeAck",
1327
+ body: params
1328
+ });
1329
+ return { success: true };
1330
+ } catch (error) {
1331
+ if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
1332
+ throw error;
1333
+ }
1334
+ await this.client.request({
1335
+ endpoint: `/v1/memories/${memoryId}`,
1336
+ method: "PUT",
1337
+ operation: "writeAck",
1338
+ body: { content: params.content }
1339
+ });
1340
+ return { success: true };
1341
+ }
1342
+ }
1343
+ async delete(memoryId) {
1344
+ try {
1345
+ await this.client.request({
1346
+ endpoint: `/v1/memory/${memoryId}`,
1347
+ method: "DELETE",
1348
+ operation: "writeAck"
1349
+ });
1350
+ return { success: true, deleted: memoryId };
1351
+ } catch (error) {
1352
+ if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
1353
+ throw error;
1354
+ }
1355
+ await this.client.request({
1356
+ endpoint: `/v1/memories/${memoryId}`,
1357
+ method: "DELETE",
1358
+ operation: "writeAck"
1359
+ });
1360
+ return { success: true, deleted: memoryId };
1361
+ }
1362
+ }
1363
+ async flag(params) {
1364
+ try {
1365
+ await this.client.request({
1366
+ endpoint: `/v1/memory/${params.memoryId}/flag`,
1367
+ method: "POST",
1368
+ operation: "writeAck",
1369
+ body: {
1370
+ reason: params.reason,
1371
+ severity: params.severity || "medium"
1372
+ }
1373
+ });
1374
+ return { success: true };
220
1375
  } catch (error) {
221
- const fallback = await this.fallbackCaptureViaAddMemory(messages, options);
222
- if (fallback.success) {
223
- return fallback;
1376
+ if (this.client.getCompatMode() !== "fallback" || !isEndpointNotFound(error)) {
1377
+ throw error;
224
1378
  }
225
- console.error("[Whisper] Session capture failed:", error);
226
- return { success: false, extracted: 0 };
1379
+ await this.client.request({
1380
+ endpoint: `/v1/memory/${params.memoryId}`,
1381
+ method: "PUT",
1382
+ operation: "writeAck",
1383
+ body: {
1384
+ content: `[FLAGGED:${params.severity || "medium"}] ${params.reason}`
1385
+ }
1386
+ });
1387
+ return { success: true };
227
1388
  }
228
1389
  }
229
- /**
230
- * Run a full agent turn with automatic memory read (before) + write (after).
231
- */
232
- async runTurn(params) {
233
- const contextResult = await this.getContext(params.userMessage, {
1390
+ };
1391
+
1392
+ // ../src/sdk/modules/session.ts
1393
+ function randomSessionId() {
1394
+ return `sess_${stableHash(`${Date.now()}_${Math.random()}`)}`;
1395
+ }
1396
+ function assertTransition(current, next) {
1397
+ const allowed = {
1398
+ created: ["active", "ended", "archived"],
1399
+ active: ["suspended", "ended", "archived"],
1400
+ suspended: ["resumed", "ended", "archived"],
1401
+ resumed: ["suspended", "ended", "archived"],
1402
+ ended: ["archived"],
1403
+ archived: []
1404
+ };
1405
+ if (!allowed[current].includes(next)) {
1406
+ throw new RuntimeClientError({
1407
+ code: "INVALID_SESSION_STATE",
1408
+ message: `Invalid session transition ${current} -> ${next}`,
1409
+ retryable: false
1410
+ });
1411
+ }
1412
+ }
1413
+ var SessionModule = class {
1414
+ constructor(memory, defaultProject) {
1415
+ this.memory = memory;
1416
+ this.defaultProject = defaultProject;
1417
+ }
1418
+ sessions = /* @__PURE__ */ new Map();
1419
+ resolveProject(project) {
1420
+ const value = project || this.defaultProject;
1421
+ if (!value) {
1422
+ throw new RuntimeClientError({
1423
+ code: "MISSING_PROJECT",
1424
+ message: "Project is required",
1425
+ retryable: false
1426
+ });
1427
+ }
1428
+ return value;
1429
+ }
1430
+ ensure(sessionId) {
1431
+ const found = this.sessions.get(sessionId);
1432
+ if (!found) {
1433
+ throw new RuntimeClientError({
1434
+ code: "SESSION_NOT_FOUND",
1435
+ message: `Unknown session ${sessionId}`,
1436
+ retryable: false
1437
+ });
1438
+ }
1439
+ return found;
1440
+ }
1441
+ async start(params) {
1442
+ const project = this.resolveProject(params.project);
1443
+ const sessionId = params.sessionId || randomSessionId();
1444
+ const now = nowIso();
1445
+ const record = {
1446
+ sessionId,
1447
+ project,
234
1448
  userId: params.userId,
235
- sessionId: params.sessionId,
236
- project: params.project,
237
- limit: params.limit
1449
+ state: "active",
1450
+ sequence: 0,
1451
+ metadata: params.metadata,
1452
+ createdAt: now,
1453
+ updatedAt: now
1454
+ };
1455
+ this.sessions.set(sessionId, record);
1456
+ return {
1457
+ sessionId,
1458
+ state: record.state,
1459
+ createdAt: now
1460
+ };
1461
+ }
1462
+ async event(params) {
1463
+ const session = this.ensure(params.sessionId);
1464
+ if (session.state !== "active" && session.state !== "resumed") {
1465
+ throw new RuntimeClientError({
1466
+ code: "INVALID_SESSION_STATE",
1467
+ message: `Cannot append event in ${session.state} state`,
1468
+ retryable: false
1469
+ });
1470
+ }
1471
+ session.sequence += 1;
1472
+ session.updatedAt = nowIso();
1473
+ const eventId = `evt_${stableHash(JSON.stringify({
1474
+ sessionId: session.sessionId,
1475
+ seq: session.sequence,
1476
+ type: params.type,
1477
+ content: params.content,
1478
+ parent: params.parentEventId || ""
1479
+ }))}`;
1480
+ await this.memory.add({
1481
+ project: session.project,
1482
+ content: `${params.type}: ${params.content}`,
1483
+ memory_type: "event",
1484
+ user_id: session.userId,
1485
+ session_id: session.sessionId,
1486
+ metadata: {
1487
+ session_event: true,
1488
+ event_id: eventId,
1489
+ sequence: session.sequence,
1490
+ parent_event_id: params.parentEventId,
1491
+ ...session.metadata,
1492
+ ...params.metadata || {}
1493
+ },
1494
+ write_mode: "async"
238
1495
  });
239
- const prompt = contextResult.context ? `${contextResult.context}
240
-
241
- User: ${params.userMessage}` : params.userMessage;
242
- const response = await params.generate(prompt);
243
- const captureResult = await this.captureSession(
244
- [
245
- { role: "user", content: params.userMessage },
246
- { role: "assistant", content: response }
247
- ],
248
- {
249
- userId: params.userId,
250
- sessionId: params.sessionId,
251
- project: params.project
252
- }
253
- );
254
1496
  return {
255
- response,
256
- context: contextResult.context,
257
- count: contextResult.count,
258
- extracted: captureResult.extracted
1497
+ success: true,
1498
+ eventId,
1499
+ sequence: session.sequence
259
1500
  };
260
1501
  }
261
- /**
262
- * Direct access to WhisperContext for advanced usage
263
- */
264
- raw() {
265
- return this.client;
1502
+ async suspend(params) {
1503
+ const session = this.ensure(params.sessionId);
1504
+ assertTransition(session.state, "suspended");
1505
+ session.state = "suspended";
1506
+ session.updatedAt = nowIso();
1507
+ return { sessionId: session.sessionId, state: session.state };
1508
+ }
1509
+ async resume(params) {
1510
+ const session = this.ensure(params.sessionId);
1511
+ const target = session.state === "suspended" ? "resumed" : "active";
1512
+ assertTransition(session.state, target);
1513
+ session.state = target;
1514
+ session.updatedAt = nowIso();
1515
+ return { sessionId: session.sessionId, state: session.state };
1516
+ }
1517
+ async end(params) {
1518
+ const session = this.ensure(params.sessionId);
1519
+ assertTransition(session.state, "ended");
1520
+ session.state = "ended";
1521
+ session.updatedAt = nowIso();
1522
+ return { sessionId: session.sessionId, state: session.state };
1523
+ }
1524
+ async archive(params) {
1525
+ const session = this.ensure(params.sessionId);
1526
+ assertTransition(session.state, "archived");
1527
+ session.state = "archived";
1528
+ session.updatedAt = nowIso();
1529
+ return { sessionId: session.sessionId, state: session.state };
266
1530
  }
267
- extractMemoryIdsFromBulkResponse(bulkResponse) {
268
- const ids = [];
269
- if (Array.isArray(bulkResponse?.memories)) {
270
- for (const memory of bulkResponse.memories) {
271
- if (memory?.id) ids.push(memory.id);
272
- }
273
- }
274
- if (bulkResponse?.memory?.id) {
275
- ids.push(bulkResponse.memory.id);
276
- }
277
- if (bulkResponse?.id) {
278
- ids.push(bulkResponse.id);
279
- }
280
- return Array.from(new Set(ids));
1531
+ };
1532
+
1533
+ // ../src/sdk/modules/profile.ts
1534
+ var ProfileModule = class {
1535
+ constructor(memory) {
1536
+ this.memory = memory;
281
1537
  }
282
- async fallbackCaptureViaAddMemory(messages, options) {
283
- const userMessages = messages.filter((m) => m.role === "user").map((m) => (m.content || "").trim()).filter((content) => content.length >= 5).slice(-2);
284
- if (userMessages.length === 0) {
285
- return { success: false, extracted: 0 };
1538
+ async getUserProfile(params) {
1539
+ return this.memory.getUserProfile(params);
1540
+ }
1541
+ async getSessionMemories(params) {
1542
+ return this.memory.getSessionMemories(params);
1543
+ }
1544
+ };
1545
+
1546
+ // ../src/sdk/modules/analytics.ts
1547
+ var AnalyticsModule = class {
1548
+ constructor(diagnostics, queue) {
1549
+ this.diagnostics = diagnostics;
1550
+ this.queue = queue;
1551
+ }
1552
+ diagnosticsSnapshot() {
1553
+ return this.diagnostics.snapshot();
1554
+ }
1555
+ queueStatus() {
1556
+ return this.queue.status();
1557
+ }
1558
+ };
1559
+
1560
+ // ../src/sdk/whisper.ts
1561
+ var WhisperClient = class _WhisperClient {
1562
+ constructor(config) {
1563
+ this.config = config;
1564
+ this.diagnosticsStore = new DiagnosticsStore(config.telemetry?.maxEntries || 1e3);
1565
+ this.runtimeClient = new RuntimeClient(
1566
+ {
1567
+ apiKey: config.apiKey,
1568
+ baseUrl: config.baseUrl,
1569
+ compatMode: config.compatMode || "fallback",
1570
+ timeouts: config.timeouts,
1571
+ retryPolicy: config.retryPolicy
1572
+ },
1573
+ this.diagnosticsStore
1574
+ );
1575
+ this.searchCache = new SearchResponseCache(
1576
+ config.cache?.ttlMs ?? 7e3,
1577
+ config.cache?.capacity ?? 500
1578
+ );
1579
+ const queueStore = config.queue?.persistence === "storage" ? createStorageQueueStore() : config.queue?.persistence === "file" && config.queue.filePath ? createFileQueueStore(config.queue.filePath) : new InMemoryQueueStore();
1580
+ this.writeQueue = new WriteQueue({
1581
+ store: queueStore,
1582
+ maxBatchSize: config.queue?.maxBatchSize ?? 50,
1583
+ flushIntervalMs: config.queue?.flushIntervalMs ?? 100,
1584
+ maxAttempts: config.queue?.maxAttempts ?? 2,
1585
+ flushHandler: async (items) => {
1586
+ if (items.length === 0) return;
1587
+ const project = items[0].project;
1588
+ const memories = items.map((item) => ({
1589
+ ...item.payload,
1590
+ user_id: item.payload.user_id ?? item.userId,
1591
+ session_id: item.payload.session_id ?? item.sessionId,
1592
+ metadata: {
1593
+ ...item.payload.metadata || {},
1594
+ event_id: item.eventId,
1595
+ queued_at: item.createdAt
1596
+ }
1597
+ }));
1598
+ try {
1599
+ await this.runtimeClient.request({
1600
+ endpoint: "/v1/memory/bulk",
1601
+ method: "POST",
1602
+ operation: "bulk",
1603
+ body: {
1604
+ project,
1605
+ write_mode: "async",
1606
+ memories
1607
+ }
1608
+ });
1609
+ } catch (error) {
1610
+ if (this.runtimeClient.getCompatMode() !== "fallback" || !(error instanceof RuntimeClientError) || error.status !== 404) {
1611
+ throw error;
1612
+ }
1613
+ await Promise.all(
1614
+ memories.map(async (memory) => {
1615
+ try {
1616
+ await this.runtimeClient.request({
1617
+ endpoint: "/v1/memory",
1618
+ method: "POST",
1619
+ operation: "writeAck",
1620
+ body: {
1621
+ project,
1622
+ ...memory,
1623
+ write_mode: "sync"
1624
+ }
1625
+ });
1626
+ } catch (fallbackError) {
1627
+ if (this.runtimeClient.getCompatMode() !== "fallback" || !(fallbackError instanceof RuntimeClientError) || fallbackError.status !== 404) {
1628
+ throw fallbackError;
1629
+ }
1630
+ await this.runtimeClient.request({
1631
+ endpoint: "/v1/memories",
1632
+ method: "POST",
1633
+ operation: "writeAck",
1634
+ body: {
1635
+ project,
1636
+ ...memory,
1637
+ memory_type: memory.memory_type === "event" ? "episodic" : memory.memory_type
1638
+ }
1639
+ });
1640
+ }
1641
+ })
1642
+ );
1643
+ }
1644
+ }
1645
+ });
1646
+ if (config.queue?.enabled !== false) {
1647
+ void this.writeQueue.start();
286
1648
  }
287
- let extracted = 0;
288
- for (const content of userMessages) {
289
- try {
290
- await this.client.addMemory({
291
- project: options?.project ?? this.options.project,
292
- content,
293
- memory_type: "factual",
294
- user_id: options?.userId ?? this.userId,
295
- session_id: options?.sessionId ?? this.sessionId,
296
- allow_legacy_fallback: true
297
- });
298
- extracted += 1;
299
- } catch {
1649
+ this.memoryModule = new MemoryModule(
1650
+ this.runtimeClient,
1651
+ this.searchCache,
1652
+ this.writeQueue,
1653
+ {
1654
+ defaultProject: config.project,
1655
+ cacheEnabled: config.cache?.enabled !== false,
1656
+ queueEnabled: config.queue?.enabled !== false
300
1657
  }
1658
+ );
1659
+ this.sessionModule = new SessionModule(this.memoryModule, config.project);
1660
+ this.profileModule = new ProfileModule(this.memoryModule);
1661
+ this.analyticsModule = new AnalyticsModule(this.diagnosticsStore, this.writeQueue);
1662
+ this.diagnostics = {
1663
+ getLast: (limit) => this.diagnosticsStore.getLast(limit),
1664
+ subscribe: (fn) => this.diagnosticsStore.subscribe(fn),
1665
+ snapshot: () => this.diagnosticsStore.snapshot()
1666
+ };
1667
+ this.queue = {
1668
+ flush: () => this.writeQueue.flush(),
1669
+ status: () => this.writeQueue.status()
1670
+ };
1671
+ this.memory = {
1672
+ add: (params) => this.memoryModule.add(params),
1673
+ addBulk: (params) => this.memoryModule.addBulk(params),
1674
+ search: (params) => this.memoryModule.search(params),
1675
+ getUserProfile: (params) => this.memoryModule.getUserProfile(params),
1676
+ getSessionMemories: (params) => this.memoryModule.getSessionMemories(params),
1677
+ update: (memoryId, params) => this.memoryModule.update(memoryId, params),
1678
+ delete: (memoryId) => this.memoryModule.delete(memoryId),
1679
+ flag: (params) => this.memoryModule.flag(params)
1680
+ };
1681
+ this.session = {
1682
+ start: (params) => this.sessionModule.start(params),
1683
+ event: (params) => this.sessionModule.event(params),
1684
+ suspend: (params) => this.sessionModule.suspend(params),
1685
+ resume: (params) => this.sessionModule.resume(params),
1686
+ end: (params) => this.sessionModule.end(params)
1687
+ };
1688
+ this.profile = {
1689
+ getUserProfile: (params) => this.profileModule.getUserProfile(params),
1690
+ getSessionMemories: (params) => this.profileModule.getSessionMemories(params)
1691
+ };
1692
+ this.analytics = {
1693
+ diagnosticsSnapshot: () => this.analyticsModule.diagnosticsSnapshot(),
1694
+ queueStatus: () => this.analyticsModule.queueStatus()
1695
+ };
1696
+ }
1697
+ diagnostics;
1698
+ queue;
1699
+ memory;
1700
+ session;
1701
+ profile;
1702
+ analytics;
1703
+ runtimeClient;
1704
+ diagnosticsStore;
1705
+ searchCache;
1706
+ writeQueue;
1707
+ memoryModule;
1708
+ sessionModule;
1709
+ profileModule;
1710
+ analyticsModule;
1711
+ static fromEnv(overrides = {}) {
1712
+ const env = typeof process !== "undefined" ? process.env : {};
1713
+ const apiKey = overrides.apiKey || env.WHISPER_API_KEY || env.USEWHISPER_API_KEY || env.API_KEY;
1714
+ if (!apiKey) {
1715
+ throw new Error("Missing API key. Set WHISPER_API_KEY / USEWHISPER_API_KEY / API_KEY.");
301
1716
  }
302
- return { success: extracted > 0, extracted };
1717
+ return new _WhisperClient({
1718
+ apiKey,
1719
+ baseUrl: overrides.baseUrl || env.WHISPER_BASE_URL || env.API_BASE_URL || "https://context.usewhisper.dev",
1720
+ project: overrides.project || env.WHISPER_PROJECT || env.PROJECT,
1721
+ ...overrides
1722
+ });
1723
+ }
1724
+ withRunContext(context) {
1725
+ const base = this;
1726
+ return {
1727
+ memory: {
1728
+ add: (params) => base.memory.add({
1729
+ ...params,
1730
+ project: params.project || context.project || base.config.project,
1731
+ user_id: params.user_id || context.userId,
1732
+ session_id: params.session_id || context.sessionId
1733
+ }),
1734
+ search: (params) => base.memory.search({
1735
+ ...params,
1736
+ project: params.project || context.project || base.config.project,
1737
+ user_id: params.user_id || context.userId,
1738
+ session_id: params.session_id || context.sessionId
1739
+ })
1740
+ },
1741
+ session: {
1742
+ event: (params) => base.session.event({
1743
+ ...params,
1744
+ sessionId: params.sessionId || context.sessionId || ""
1745
+ })
1746
+ },
1747
+ queue: base.queue,
1748
+ diagnostics: base.diagnostics
1749
+ };
1750
+ }
1751
+ async shutdown() {
1752
+ await this.writeQueue.stop();
303
1753
  }
304
1754
  };
305
- var whisper_agent_default = Whisper;
1755
+ var whisper_default = WhisperClient;
306
1756
 
307
1757
  // ../src/sdk/middleware.ts
308
1758
  var WhisperAgentMiddleware = class {
@@ -373,14 +1823,409 @@ function createAgentMiddleware(config) {
373
1823
  return new WhisperAgentMiddleware(config);
374
1824
  }
375
1825
 
1826
+ // ../src/sdk/adapters/langchain.ts
1827
+ var LangChainMemoryAdapter = class {
1828
+ constructor(client, options) {
1829
+ this.client = client;
1830
+ this.options = options;
1831
+ this.memoryKey = options.memoryKey || "history";
1832
+ this.memoryKeys = [this.memoryKey];
1833
+ }
1834
+ memoryKeys;
1835
+ memoryKey;
1836
+ async loadMemoryVariables(_inputValues) {
1837
+ const response = await this.client.memory.search({
1838
+ project: this.options.project,
1839
+ query: "recent context",
1840
+ user_id: this.options.userId,
1841
+ session_id: this.options.sessionId,
1842
+ top_k: this.options.topK || 10,
1843
+ profile: this.options.profile || "fast",
1844
+ include_pending: true
1845
+ });
1846
+ const content = (response.results || []).map((row) => row.memory?.content || "").filter(Boolean).join("\n");
1847
+ return {
1848
+ [this.memoryKey]: content
1849
+ };
1850
+ }
1851
+ async saveContext(inputValues, outputValues) {
1852
+ const userInput = typeof inputValues.input === "string" ? inputValues.input : JSON.stringify(inputValues);
1853
+ const output = typeof outputValues.output === "string" ? outputValues.output : JSON.stringify(outputValues);
1854
+ await this.client.memory.addBulk({
1855
+ project: this.options.project,
1856
+ write_mode: "async",
1857
+ memories: [
1858
+ {
1859
+ content: `user: ${userInput}`,
1860
+ memory_type: "event",
1861
+ user_id: this.options.userId,
1862
+ session_id: this.options.sessionId
1863
+ },
1864
+ {
1865
+ content: `assistant: ${output}`,
1866
+ memory_type: "event",
1867
+ user_id: this.options.userId,
1868
+ session_id: this.options.sessionId
1869
+ }
1870
+ ]
1871
+ });
1872
+ }
1873
+ async clear() {
1874
+ const profile = await this.client.memory.getUserProfile({
1875
+ project: this.options.project,
1876
+ user_id: this.options.userId
1877
+ });
1878
+ await Promise.all(
1879
+ (profile.memories || []).map((memory) => String(memory.id || "")).filter(Boolean).map((id) => this.client.memory.delete(id))
1880
+ );
1881
+ }
1882
+ };
1883
+ function createLangChainMemoryAdapter(client, options) {
1884
+ return new LangChainMemoryAdapter(client, options);
1885
+ }
1886
+
1887
+ // ../src/sdk/adapters/langgraph.ts
1888
+ function asRecord(value) {
1889
+ return value && typeof value === "object" ? value : {};
1890
+ }
1891
+ function text(value) {
1892
+ return typeof value === "string" && value.trim() ? value : void 0;
1893
+ }
1894
+ function isTupleLike(value) {
1895
+ if (!value || typeof value !== "object") return false;
1896
+ const tuple = value;
1897
+ const config = asRecord(tuple.config);
1898
+ const configurable = asRecord(config.configurable);
1899
+ return Boolean(
1900
+ typeof configurable.thread_id === "string" && tuple.checkpoint && typeof tuple.checkpoint === "object"
1901
+ );
1902
+ }
1903
+ function tupleTimestamp(tuple, fallback) {
1904
+ const checkpoint = asRecord(tuple.checkpoint);
1905
+ const metadata = asRecord(tuple.metadata);
1906
+ const sources = [
1907
+ checkpoint.updatedAt,
1908
+ checkpoint.updated_at,
1909
+ checkpoint.ts,
1910
+ checkpoint.timestamp,
1911
+ metadata.updatedAt,
1912
+ metadata.updated_at,
1913
+ metadata.ts,
1914
+ metadata.timestamp,
1915
+ fallback
1916
+ ];
1917
+ for (const source of sources) {
1918
+ const parsed = source ? new Date(String(source)).getTime() : Number.NaN;
1919
+ if (!Number.isNaN(parsed) && parsed > 0) return parsed;
1920
+ }
1921
+ return 0;
1922
+ }
1923
+ function metadataMatches(target, filter) {
1924
+ if (!filter || Object.keys(filter).length === 0) return true;
1925
+ const value = target || {};
1926
+ return Object.entries(filter).every(([key, expected]) => {
1927
+ if (!(key in value)) return false;
1928
+ return JSON.stringify(value[key]) === JSON.stringify(expected);
1929
+ });
1930
+ }
1931
+ var LangGraphCheckpointAdapter = class {
1932
+ constructor(client, options = {}) {
1933
+ this.client = client;
1934
+ this.options = options;
1935
+ }
1936
+ localByKey = /* @__PURE__ */ new Map();
1937
+ localByThread = /* @__PURE__ */ new Map();
1938
+ options;
1939
+ getUserId(threadId) {
1940
+ const prefix = this.options.userIdPrefix || "langgraph-thread";
1941
+ return `${prefix}:${threadId}`;
1942
+ }
1943
+ resolveCheckpointNs(config) {
1944
+ return config.configurable.checkpoint_ns || this.options.defaultCheckpointNs || "default";
1945
+ }
1946
+ makeLocalKey(threadId, checkpointNs, checkpointId) {
1947
+ return `${threadId}:${checkpointNs}:${checkpointId}`;
1948
+ }
1949
+ normalizeTuple(tuple) {
1950
+ const config = asRecord(tuple.config);
1951
+ const configurable = asRecord(config.configurable);
1952
+ const threadId = text(configurable.thread_id) || "";
1953
+ const checkpointNs = text(configurable.checkpoint_ns) || this.options.defaultCheckpointNs || "default";
1954
+ const checkpointId = text(configurable.checkpoint_id) || "";
1955
+ return {
1956
+ config: {
1957
+ configurable: {
1958
+ thread_id: threadId,
1959
+ checkpoint_ns: checkpointNs,
1960
+ checkpoint_id: checkpointId || void 0
1961
+ }
1962
+ },
1963
+ checkpoint: asRecord(tuple.checkpoint),
1964
+ metadata: asRecord(tuple.metadata),
1965
+ parent_config: tuple.parent_config ? {
1966
+ configurable: {
1967
+ thread_id: text(tuple.parent_config.configurable.thread_id) || "",
1968
+ checkpoint_ns: text(tuple.parent_config.configurable.checkpoint_ns) || checkpointNs,
1969
+ checkpoint_id: text(tuple.parent_config.configurable.checkpoint_id)
1970
+ }
1971
+ } : null
1972
+ };
1973
+ }
1974
+ parseCheckpointTupleFromRow(row) {
1975
+ const rowMetadata = asRecord(row.metadata);
1976
+ const marker = rowMetadata.langgraph_checkpoint === true;
1977
+ const rawContent = text(row.content) || "";
1978
+ if (!rawContent) return null;
1979
+ try {
1980
+ const parsed = JSON.parse(rawContent);
1981
+ let tuple = null;
1982
+ if (isTupleLike(parsed)) {
1983
+ tuple = this.normalizeTuple(parsed);
1984
+ } else {
1985
+ const wrapped = asRecord(parsed);
1986
+ if (isTupleLike(wrapped.tuple)) {
1987
+ tuple = this.normalizeTuple(wrapped.tuple);
1988
+ }
1989
+ }
1990
+ if (!tuple) return null;
1991
+ const config = tuple.config.configurable;
1992
+ if (!config.thread_id) return null;
1993
+ if (!config.checkpoint_id) return null;
1994
+ if (!marker && rowMetadata.checkpoint_id !== config.checkpoint_id) {
1995
+ return null;
1996
+ }
1997
+ return {
1998
+ tuple,
1999
+ memoryId: text(row.id),
2000
+ createdAt: text(row.createdAt) || text(row.created_at),
2001
+ updatedAt: text(row.updatedAt) || text(row.updated_at)
2002
+ };
2003
+ } catch {
2004
+ return null;
2005
+ }
2006
+ }
2007
+ upsertLocal(record) {
2008
+ const cfg = record.tuple.config.configurable;
2009
+ const key = this.makeLocalKey(cfg.thread_id, cfg.checkpoint_ns || "default", cfg.checkpoint_id || "");
2010
+ this.localByKey.set(key, record);
2011
+ if (!this.localByThread.has(cfg.thread_id)) {
2012
+ this.localByThread.set(cfg.thread_id, /* @__PURE__ */ new Set());
2013
+ }
2014
+ this.localByThread.get(cfg.thread_id).add(key);
2015
+ }
2016
+ mergeWithLocal(records, threadId) {
2017
+ const merged = /* @__PURE__ */ new Map();
2018
+ for (const record of records) {
2019
+ const cfg = record.tuple.config.configurable;
2020
+ const key = this.makeLocalKey(cfg.thread_id, cfg.checkpoint_ns || "default", cfg.checkpoint_id || "");
2021
+ merged.set(key, record);
2022
+ }
2023
+ const localKeys = this.localByThread.get(threadId);
2024
+ if (localKeys) {
2025
+ for (const key of localKeys) {
2026
+ const local = this.localByKey.get(key);
2027
+ if (local) {
2028
+ merged.set(key, local);
2029
+ }
2030
+ }
2031
+ }
2032
+ return Array.from(merged.values());
2033
+ }
2034
+ applyListFilters(records, options) {
2035
+ let filtered = records;
2036
+ if (options?.filter?.checkpointNs) {
2037
+ filtered = filtered.filter(
2038
+ (record) => (record.tuple.config.configurable.checkpoint_ns || "default") === options.filter.checkpointNs
2039
+ );
2040
+ }
2041
+ if (options?.filter?.metadata) {
2042
+ filtered = filtered.filter((record) => metadataMatches(record.tuple.metadata, options.filter.metadata));
2043
+ }
2044
+ if (options?.before?.checkpointId) {
2045
+ const beforeId = options.before.checkpointId;
2046
+ filtered = filtered.filter(
2047
+ (record) => record.tuple.config.configurable.checkpoint_id !== beforeId
2048
+ );
2049
+ }
2050
+ if (options?.before?.updatedAt) {
2051
+ const cutoff = new Date(options.before.updatedAt).getTime();
2052
+ if (!Number.isNaN(cutoff)) {
2053
+ filtered = filtered.filter((record) => {
2054
+ const value = tupleTimestamp(record.tuple, record.updatedAt || record.createdAt);
2055
+ return value < cutoff;
2056
+ });
2057
+ }
2058
+ }
2059
+ const direction = options?.sort || "desc";
2060
+ filtered.sort((a, b) => {
2061
+ const ta = tupleTimestamp(a.tuple, a.updatedAt || a.createdAt);
2062
+ const tb = tupleTimestamp(b.tuple, b.updatedAt || b.createdAt);
2063
+ return direction === "asc" ? ta - tb : tb - ta;
2064
+ });
2065
+ if (options?.limit && options.limit > 0) {
2066
+ return filtered.slice(0, options.limit);
2067
+ }
2068
+ return filtered;
2069
+ }
2070
+ async fetchThreadRecords(threadId) {
2071
+ const profile = await this.client.memory.getUserProfile({
2072
+ project: this.options.project,
2073
+ user_id: this.getUserId(threadId),
2074
+ include_pending: true
2075
+ });
2076
+ const parsed = (profile.memories || []).map((row) => this.parseCheckpointTupleFromRow(row)).filter((value) => value !== null).filter((record) => record.tuple.config.configurable.thread_id === threadId);
2077
+ const merged = this.mergeWithLocal(parsed, threadId);
2078
+ for (const record of merged) {
2079
+ this.upsertLocal(record);
2080
+ }
2081
+ return merged;
2082
+ }
2083
+ async get(config) {
2084
+ const threadId = config.configurable.thread_id;
2085
+ const checkpointNs = this.resolveCheckpointNs(config);
2086
+ const checkpointId = config.configurable.checkpoint_id;
2087
+ if (checkpointId) {
2088
+ const local = this.localByKey.get(this.makeLocalKey(threadId, checkpointNs, checkpointId));
2089
+ if (local) return local.tuple;
2090
+ }
2091
+ const records = await this.fetchThreadRecords(threadId);
2092
+ const scoped = records.filter((record) => {
2093
+ const cfg = record.tuple.config.configurable;
2094
+ if ((cfg.checkpoint_ns || "default") !== checkpointNs) return false;
2095
+ if (!checkpointId) return true;
2096
+ return cfg.checkpoint_id === checkpointId;
2097
+ });
2098
+ if (scoped.length === 0) return void 0;
2099
+ scoped.sort((a, b) => {
2100
+ const ta = tupleTimestamp(a.tuple, a.updatedAt || a.createdAt);
2101
+ const tb = tupleTimestamp(b.tuple, b.updatedAt || b.createdAt);
2102
+ return tb - ta;
2103
+ });
2104
+ return scoped[0].tuple;
2105
+ }
2106
+ async put(config, checkpoint, metadata, parentConfig) {
2107
+ const threadId = config.configurable.thread_id;
2108
+ const checkpointNs = this.resolveCheckpointNs(config);
2109
+ const checkpointId = config.configurable.checkpoint_id || text(checkpoint.id) || `cp_${stableHash(JSON.stringify({
2110
+ threadId,
2111
+ checkpointNs,
2112
+ checkpoint,
2113
+ metadata: metadata || {},
2114
+ parentConfig: parentConfig || null
2115
+ }))}`;
2116
+ const tuple = this.normalizeTuple({
2117
+ config: {
2118
+ configurable: {
2119
+ thread_id: threadId,
2120
+ checkpoint_ns: checkpointNs,
2121
+ checkpoint_id: checkpointId
2122
+ }
2123
+ },
2124
+ checkpoint: {
2125
+ ...checkpoint,
2126
+ id: checkpointId
2127
+ },
2128
+ metadata: {
2129
+ ...metadata || {},
2130
+ checkpoint_ns: checkpointNs,
2131
+ written_at: nowIso()
2132
+ },
2133
+ parent_config: parentConfig ? this.normalizeTuple({
2134
+ config: parentConfig,
2135
+ checkpoint: {},
2136
+ metadata: {},
2137
+ parent_config: null
2138
+ }).config : null
2139
+ });
2140
+ const record = { tuple, updatedAt: nowIso() };
2141
+ this.upsertLocal(record);
2142
+ await this.client.memory.add({
2143
+ project: this.options.project,
2144
+ user_id: this.getUserId(threadId),
2145
+ session_id: threadId,
2146
+ memory_type: "event",
2147
+ write_mode: "async",
2148
+ content: JSON.stringify(tuple),
2149
+ metadata: {
2150
+ langgraph_checkpoint: true,
2151
+ thread_id: threadId,
2152
+ checkpoint_ns: checkpointNs,
2153
+ checkpoint_id: checkpointId,
2154
+ parent_checkpoint_id: parentConfig?.configurable.checkpoint_id
2155
+ }
2156
+ });
2157
+ return tuple.config;
2158
+ }
2159
+ async list(config, options) {
2160
+ const threadId = config.configurable.thread_id;
2161
+ const checkpointNs = this.resolveCheckpointNs(config);
2162
+ const records = await this.fetchThreadRecords(threadId);
2163
+ const scoped = records.filter(
2164
+ (record) => (record.tuple.config.configurable.checkpoint_ns || "default") === checkpointNs
2165
+ );
2166
+ return this.applyListFilters(scoped, options).map((record) => record.tuple);
2167
+ }
2168
+ async search(params) {
2169
+ const includePending = params.includePending !== false;
2170
+ const profile = params.profile || "fast";
2171
+ const checkpointNs = params.checkpointNs || this.options.defaultCheckpointNs || "default";
2172
+ const response = await this.client.memory.search({
2173
+ project: this.options.project,
2174
+ query: params.query,
2175
+ user_id: this.getUserId(params.threadId),
2176
+ session_id: params.threadId,
2177
+ top_k: params.topK || 10,
2178
+ include_pending: includePending,
2179
+ profile
2180
+ });
2181
+ const serverHits = (response.results || []).map((row) => {
2182
+ const content = text(row.memory?.content);
2183
+ if (!content) return null;
2184
+ try {
2185
+ const parsed = JSON.parse(content);
2186
+ if (!isTupleLike(parsed)) return null;
2187
+ const tuple = this.normalizeTuple(parsed);
2188
+ const ns = tuple.config.configurable.checkpoint_ns || "default";
2189
+ if (tuple.config.configurable.thread_id !== params.threadId) return null;
2190
+ if (ns !== checkpointNs) return null;
2191
+ return { tuple, score: row.similarity || 0 };
2192
+ } catch {
2193
+ return null;
2194
+ }
2195
+ }).filter((value) => value !== null);
2196
+ const merged = /* @__PURE__ */ new Map();
2197
+ for (const hit of serverHits) {
2198
+ const cfg = hit.tuple.config.configurable;
2199
+ const key = this.makeLocalKey(cfg.thread_id, cfg.checkpoint_ns || "default", cfg.checkpoint_id || "");
2200
+ merged.set(key, hit);
2201
+ }
2202
+ const localKeys = this.localByThread.get(params.threadId);
2203
+ if (localKeys && includePending) {
2204
+ for (const key of localKeys) {
2205
+ const local = this.localByKey.get(key);
2206
+ if (!local) continue;
2207
+ const cfg = local.tuple.config.configurable;
2208
+ if ((cfg.checkpoint_ns || "default") !== checkpointNs) continue;
2209
+ if (!merged.has(key)) {
2210
+ merged.set(key, { tuple: local.tuple, score: 0 });
2211
+ }
2212
+ }
2213
+ }
2214
+ return Array.from(merged.values()).sort((a, b) => b.score - a.score).map((row) => row.tuple).slice(0, params.topK || 10);
2215
+ }
2216
+ };
2217
+ function createLangGraphCheckpointAdapter(client, options = {}) {
2218
+ return new LangGraphCheckpointAdapter(client, options);
2219
+ }
2220
+
376
2221
  // ../src/sdk/graph-utils.ts
377
2222
  function sanitizeId(id) {
378
2223
  return `n_${id.replace(/[^a-zA-Z0-9_]/g, "_")}`;
379
2224
  }
380
2225
  function shortLabel(input, max = 48) {
381
- const text = (input || "").replace(/\s+/g, " ").trim();
382
- if (text.length <= max) return text;
383
- return `${text.slice(0, max - 3)}...`;
2226
+ const text2 = (input || "").replace(/\s+/g, " ").trim();
2227
+ if (text2.length <= max) return text2;
2228
+ return `${text2.slice(0, max - 3)}...`;
384
2229
  }
385
2230
  function memoryGraphToMermaid(graph) {
386
2231
  const lines = ["flowchart LR"];
@@ -418,24 +2263,25 @@ var DEFAULT_BASE_DELAY_MS = 250;
418
2263
  var DEFAULT_MAX_DELAY_MS = 2e3;
419
2264
  var DEFAULT_TIMEOUT_MS = 15e3;
420
2265
  var PROJECT_CACHE_TTL_MS = 3e4;
421
- function sleep(ms) {
422
- return new Promise((resolve) => setTimeout(resolve, ms));
423
- }
424
- function getBackoffDelay(attempt, base, max) {
425
- const jitter = 0.8 + Math.random() * 0.4;
426
- return Math.min(max, Math.floor(base * Math.pow(2, attempt) * jitter));
2266
+ var DEPRECATION_WARNINGS2 = /* @__PURE__ */ new Set();
2267
+ function warnDeprecatedOnce2(key, message) {
2268
+ if (DEPRECATION_WARNINGS2.has(key)) return;
2269
+ DEPRECATION_WARNINGS2.add(key);
2270
+ if (typeof console !== "undefined" && typeof console.warn === "function") {
2271
+ console.warn(message);
2272
+ }
427
2273
  }
428
2274
  function isLikelyProjectId(projectRef) {
429
2275
  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);
430
2276
  }
431
- function normalizeBaseUrl(url) {
2277
+ function normalizeBaseUrl2(url) {
432
2278
  let normalized = url.trim().replace(/\/+$/, "");
433
2279
  normalized = normalized.replace(/\/api\/v1$/i, "");
434
2280
  normalized = normalized.replace(/\/v1$/i, "");
435
2281
  normalized = normalized.replace(/\/api$/i, "");
436
2282
  return normalized;
437
2283
  }
438
- function normalizeEndpoint(endpoint) {
2284
+ function normalizeEndpoint2(endpoint) {
439
2285
  const withLeadingSlash = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
440
2286
  if (/^\/api\/v1(\/|$)/i.test(withLeadingSlash)) {
441
2287
  return withLeadingSlash.replace(/^\/api/i, "");
@@ -452,6 +2298,7 @@ var WhisperContext = class _WhisperContext {
452
2298
  defaultProject;
453
2299
  timeoutMs;
454
2300
  retryConfig;
2301
+ runtimeClient;
455
2302
  projectRefToId = /* @__PURE__ */ new Map();
456
2303
  projectCache = [];
457
2304
  projectCacheExpiresAt = 0;
@@ -463,7 +2310,7 @@ var WhisperContext = class _WhisperContext {
463
2310
  });
464
2311
  }
465
2312
  this.apiKey = config.apiKey;
466
- this.baseUrl = normalizeBaseUrl(config.baseUrl || "https://context.usewhisper.dev");
2313
+ this.baseUrl = normalizeBaseUrl2(config.baseUrl || "https://context.usewhisper.dev");
467
2314
  this.defaultProject = config.project;
468
2315
  this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
469
2316
  this.retryConfig = {
@@ -471,6 +2318,35 @@ var WhisperContext = class _WhisperContext {
471
2318
  baseDelayMs: config.retry?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS,
472
2319
  maxDelayMs: config.retry?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS
473
2320
  };
2321
+ this.runtimeClient = new RuntimeClient({
2322
+ apiKey: this.apiKey,
2323
+ baseUrl: this.baseUrl,
2324
+ compatMode: "fallback",
2325
+ timeouts: {
2326
+ searchMs: this.timeoutMs,
2327
+ writeAckMs: this.timeoutMs,
2328
+ bulkMs: Math.max(this.timeoutMs, 1e4),
2329
+ profileMs: this.timeoutMs,
2330
+ sessionMs: this.timeoutMs
2331
+ },
2332
+ retryPolicy: {
2333
+ baseBackoffMs: this.retryConfig.baseDelayMs,
2334
+ maxBackoffMs: this.retryConfig.maxDelayMs,
2335
+ maxAttemptsByOperation: {
2336
+ search: this.retryConfig.maxAttempts,
2337
+ writeAck: this.retryConfig.maxAttempts,
2338
+ bulk: this.retryConfig.maxAttempts,
2339
+ profile: this.retryConfig.maxAttempts,
2340
+ session: this.retryConfig.maxAttempts,
2341
+ query: this.retryConfig.maxAttempts,
2342
+ get: this.retryConfig.maxAttempts
2343
+ }
2344
+ }
2345
+ });
2346
+ warnDeprecatedOnce2(
2347
+ "whisper_context_class",
2348
+ "[Whisper SDK] WhisperContext remains supported in v2 but is legacy. Prefer WhisperClient for runtime features (queue/cache/session/diagnostics)."
2349
+ );
474
2350
  }
475
2351
  withProject(project) {
476
2352
  return new _WhisperContext({
@@ -609,76 +2485,58 @@ var WhisperContext = class _WhisperContext {
609
2485
  const message = (error.message || "").toLowerCase();
610
2486
  return !isProjectNotFoundMessage(message);
611
2487
  }
2488
+ inferOperation(endpoint, method) {
2489
+ const normalized = normalizeEndpoint2(endpoint).toLowerCase();
2490
+ if (normalized.includes("/memory/search")) return "search";
2491
+ if (normalized.includes("/memory/bulk")) return "bulk";
2492
+ if (normalized.includes("/memory/profile") || normalized.includes("/memory/session")) return "profile";
2493
+ if (normalized.includes("/memory/ingest/session")) return "session";
2494
+ if (normalized.includes("/context/query")) return "query";
2495
+ if (method === "GET") return "get";
2496
+ return "writeAck";
2497
+ }
612
2498
  async request(endpoint, options = {}) {
613
- const maxAttempts = Math.max(1, this.retryConfig.maxAttempts);
614
- const normalizedEndpoint = normalizeEndpoint(endpoint);
615
- let lastError;
616
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
617
- const controller = new AbortController();
618
- const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
2499
+ const method = String(options.method || "GET").toUpperCase();
2500
+ const normalizedEndpoint = normalizeEndpoint2(endpoint);
2501
+ const operation = this.inferOperation(normalizedEndpoint, method);
2502
+ let body;
2503
+ if (typeof options.body === "string") {
619
2504
  try {
620
- const headers = {
621
- "Content-Type": "application/json",
622
- ...options.headers
623
- };
624
- const hasAuthHeader = Object.keys(headers).some((k) => k.toLowerCase() === "authorization");
625
- const hasApiKeyHeader = Object.keys(headers).some((k) => k.toLowerCase() === "x-api-key");
626
- if (!hasAuthHeader) {
627
- headers.Authorization = `Bearer ${this.apiKey}`;
628
- }
629
- if (!hasApiKeyHeader) {
630
- headers["X-API-Key"] = this.apiKey;
631
- }
632
- const response = await fetch(`${this.baseUrl}${normalizedEndpoint}`, {
633
- ...options,
634
- signal: controller.signal,
635
- headers
636
- });
637
- clearTimeout(timeout);
638
- if (!response.ok) {
639
- let payload = null;
640
- try {
641
- payload = await response.json();
642
- } catch {
643
- payload = await response.text().catch(() => "");
644
- }
645
- let message = typeof payload === "string" ? payload : payload?.error || payload?.message || `HTTP ${response.status}: ${response.statusText}`;
646
- if (response.status === 404 && !isProjectNotFoundMessage(message)) {
647
- const endpointHint = `${this.baseUrl}${normalizedEndpoint}`;
648
- message = `Endpoint not found at ${endpointHint}. This deployment may not support this API route.`;
649
- }
650
- const { code, retryable } = this.classifyError(response.status, message);
651
- const err = new WhisperError({
652
- code,
653
- message,
654
- status: response.status,
655
- retryable,
656
- details: payload
657
- });
658
- if (!retryable || attempt === maxAttempts - 1) {
659
- throw err;
660
- }
661
- await sleep(getBackoffDelay(attempt, this.retryConfig.baseDelayMs, this.retryConfig.maxDelayMs));
662
- continue;
663
- }
664
- return response.json();
665
- } catch (error) {
666
- clearTimeout(timeout);
667
- const isAbort = error?.name === "AbortError";
668
- const mapped = error instanceof WhisperError ? error : new WhisperError({
669
- code: isAbort ? "TIMEOUT" : "NETWORK_ERROR",
670
- message: isAbort ? "Request timed out" : error?.message || "Network request failed",
671
- retryable: true,
672
- details: error
673
- });
674
- lastError = mapped;
675
- if (!mapped.retryable || attempt === maxAttempts - 1) {
676
- throw mapped;
677
- }
678
- await sleep(getBackoffDelay(attempt, this.retryConfig.baseDelayMs, this.retryConfig.maxDelayMs));
2505
+ body = JSON.parse(options.body);
2506
+ } catch {
2507
+ body = void 0;
2508
+ }
2509
+ } else if (options.body && typeof options.body === "object" && !ArrayBuffer.isView(options.body) && !(options.body instanceof ArrayBuffer) && !(options.body instanceof FormData) && !(options.body instanceof URLSearchParams) && !(options.body instanceof Blob) && !(options.body instanceof ReadableStream)) {
2510
+ body = options.body;
2511
+ }
2512
+ try {
2513
+ const response = await this.runtimeClient.request({
2514
+ endpoint: normalizedEndpoint,
2515
+ method,
2516
+ operation,
2517
+ idempotent: method === "GET" || method === "POST" && (operation === "search" || operation === "query" || operation === "profile"),
2518
+ body,
2519
+ headers: options.headers || {}
2520
+ });
2521
+ return response.data;
2522
+ } catch (error) {
2523
+ if (!(error instanceof RuntimeClientError)) {
2524
+ throw error;
679
2525
  }
2526
+ let message = error.message;
2527
+ if (error.status === 404 && !isProjectNotFoundMessage(message)) {
2528
+ const endpointHint = `${this.baseUrl}${normalizedEndpoint}`;
2529
+ message = `Endpoint not found at ${endpointHint}. This deployment may not support this API route.`;
2530
+ }
2531
+ const { code, retryable } = this.classifyError(error.status, message);
2532
+ throw new WhisperError({
2533
+ code,
2534
+ message,
2535
+ status: error.status,
2536
+ retryable,
2537
+ details: error.details
2538
+ });
680
2539
  }
681
- throw lastError instanceof Error ? lastError : new WhisperError({ code: "REQUEST_FAILED", message: "Request failed" });
682
2540
  }
683
2541
  async query(params) {
684
2542
  const projectRef = this.getRequiredProject(params.project);
@@ -751,7 +2609,7 @@ var WhisperContext = class _WhisperContext {
751
2609
  async addMemory(params) {
752
2610
  const projectRef = this.getRequiredProject(params.project);
753
2611
  return this.withProjectRefFallback(projectRef, async (project) => {
754
- const toSotaType = (memoryType) => {
2612
+ const toSotaType2 = (memoryType) => {
755
2613
  switch (memoryType) {
756
2614
  case "episodic":
757
2615
  return "event";
@@ -763,7 +2621,7 @@ var WhisperContext = class _WhisperContext {
763
2621
  return memoryType;
764
2622
  }
765
2623
  };
766
- const toLegacyType = (memoryType) => {
2624
+ const toLegacyType2 = (memoryType) => {
767
2625
  switch (memoryType) {
768
2626
  case "event":
769
2627
  return "episodic";
@@ -784,7 +2642,7 @@ var WhisperContext = class _WhisperContext {
784
2642
  body: JSON.stringify({
785
2643
  project,
786
2644
  content: params.content,
787
- memory_type: toSotaType(params.memory_type),
2645
+ memory_type: toSotaType2(params.memory_type),
788
2646
  user_id: params.user_id,
789
2647
  session_id: params.session_id,
790
2648
  agent_id: params.agent_id,
@@ -811,7 +2669,7 @@ var WhisperContext = class _WhisperContext {
811
2669
  body: JSON.stringify({
812
2670
  project,
813
2671
  content: params.content,
814
- memory_type: toLegacyType(params.memory_type),
2672
+ memory_type: toLegacyType2(params.memory_type),
815
2673
  user_id: params.user_id,
816
2674
  session_id: params.session_id,
817
2675
  agent_id: params.agent_id,
@@ -1256,11 +3114,17 @@ var WhisperContext = class _WhisperContext {
1256
3114
  var index_default = WhisperContext;
1257
3115
  // Annotate the CommonJS export names for ESM import in node:
1258
3116
  0 && (module.exports = {
3117
+ LangChainMemoryAdapter,
3118
+ LangGraphCheckpointAdapter,
1259
3119
  Whisper,
1260
3120
  WhisperAgentMiddleware,
3121
+ WhisperClient,
1261
3122
  WhisperContext,
1262
3123
  WhisperDefault,
1263
3124
  WhisperError,
3125
+ WhisperRuntimeClient,
1264
3126
  createAgentMiddleware,
3127
+ createLangChainMemoryAdapter,
3128
+ createLangGraphCheckpointAdapter,
1265
3129
  memoryGraphToMermaid
1266
3130
  });