@sovant/sdk 1.0.5 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,106 +1,851 @@
1
- export class Sovant {
2
- apiKey;
3
- baseUrl;
4
- timeout;
5
- constructor(opts) {
6
- if (!opts?.apiKey)
7
- throw new Error("Missing apiKey");
8
- this.apiKey = opts.apiKey;
9
- this.baseUrl = (opts.baseUrl ?? process.env.SOVANT_BASE_URL ?? "https://sovant.ai").replace(/\/+$/, "");
10
- this.timeout = opts.timeoutMs ?? 30000;
11
- }
12
- async req(path, init) {
13
- const ctl = new AbortController();
14
- const id = setTimeout(() => ctl.abort(), this.timeout);
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ NetworkError: () => NetworkError,
24
+ Sovant: () => Sovant,
25
+ SovantError: () => SovantError,
26
+ TimeoutError: () => TimeoutError,
27
+ default: () => index_default
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/errors.ts
32
+ var SovantError = class _SovantError extends Error {
33
+ constructor(message, code, status, requestId) {
34
+ super(message);
35
+ this.name = "SovantError";
36
+ this.code = code;
37
+ this.status = status;
38
+ this.requestId = requestId;
39
+ if (Error.captureStackTrace) {
40
+ Error.captureStackTrace(this, _SovantError);
41
+ }
42
+ }
43
+ static fromResponse(status, body, requestId) {
44
+ const statusCodeMap = {
45
+ 400: "bad_request",
46
+ 401: "unauthorized",
47
+ 403: "forbidden",
48
+ 404: "not_found",
49
+ 409: "conflict",
50
+ 422: "validation_error",
51
+ 429: "rate_limited",
52
+ 500: "internal_error",
53
+ 502: "bad_gateway",
54
+ 503: "service_unavailable",
55
+ 504: "gateway_timeout"
56
+ };
57
+ let message = "An error occurred";
58
+ let code = statusCodeMap[status] || "unknown_error";
59
+ if (body?.error) {
60
+ if (typeof body.error === "string") {
61
+ message = body.error;
62
+ } else if (body.error.message) {
63
+ message = body.error.message;
64
+ code = body.error.code || code;
65
+ }
66
+ } else if (body?.message) {
67
+ message = body.message;
68
+ } else if (typeof body === "string") {
69
+ message = body;
70
+ }
71
+ return new _SovantError(message, code, status, requestId);
72
+ }
73
+ toJSON() {
74
+ return {
75
+ name: this.name,
76
+ message: this.message,
77
+ code: this.code,
78
+ status: this.status,
79
+ requestId: this.requestId
80
+ };
81
+ }
82
+ };
83
+ var NetworkError = class _NetworkError extends SovantError {
84
+ constructor(message, cause) {
85
+ super(message, "network_error");
86
+ this.name = "NetworkError";
87
+ if (Error.captureStackTrace) {
88
+ Error.captureStackTrace(this, _NetworkError);
89
+ }
90
+ }
91
+ };
92
+ var TimeoutError = class _TimeoutError extends SovantError {
93
+ constructor(message = "Request timed out") {
94
+ super(message, "timeout");
95
+ this.name = "TimeoutError";
96
+ if (Error.captureStackTrace) {
97
+ Error.captureStackTrace(this, _TimeoutError);
98
+ }
99
+ }
100
+ };
101
+
102
+ // src/http.ts
103
+ var RETRY_STATUS_CODES = [429, 502, 503, 504];
104
+ var DEFAULT_TIMEOUT = 3e4;
105
+ var DEFAULT_MAX_RETRIES = 3;
106
+ var IDEMPOTENT_METHODS = ["GET", "DELETE"];
107
+ var HttpClient = class {
108
+ constructor(baseUrl, apiKey, globalOptions = {}) {
109
+ this.baseUrl = baseUrl;
110
+ this.apiKey = apiKey;
111
+ this.globalOptions = globalOptions;
112
+ }
113
+ async request(path, options = {}) {
114
+ const url = `${this.baseUrl}${path}`;
115
+ const timeout = options.timeout ?? this.globalOptions.timeout ?? DEFAULT_TIMEOUT;
116
+ const maxRetries = options.maxRetries ?? this.globalOptions.maxRetries ?? DEFAULT_MAX_RETRIES;
117
+ const debug = options.debug ?? this.globalOptions.debug ?? false;
118
+ const method = options.method || "GET";
119
+ const shouldRetry = IDEMPOTENT_METHODS.includes(method) || method === "GET";
120
+ let lastError;
121
+ let attempt = 0;
122
+ while (attempt <= maxRetries) {
123
+ try {
124
+ if (debug && attempt > 0) {
125
+ console.log(`[Sovant SDK] Retry attempt ${attempt} for ${method} ${url}`);
126
+ }
127
+ const response = await this.makeRequest(url, options, timeout, debug);
128
+ if (shouldRetry && RETRY_STATUS_CODES.includes(response.status) && attempt < maxRetries) {
129
+ const delay = this.calculateRetryDelay(attempt, response.headers["retry-after"]);
130
+ if (debug) {
131
+ console.log(`[Sovant SDK] Got ${response.status}, retrying after ${delay}ms...`);
132
+ }
133
+ await this.sleep(delay);
134
+ attempt++;
135
+ continue;
136
+ }
137
+ if (response.status >= 400) {
138
+ throw SovantError.fromResponse(response.status, response.data, response.requestId);
139
+ }
140
+ return response;
141
+ } catch (error) {
142
+ lastError = error;
143
+ if (error instanceof SovantError) {
144
+ if (error.status && error.status < 500) {
145
+ throw error;
146
+ }
147
+ if (!shouldRetry || attempt >= maxRetries) {
148
+ throw error;
149
+ }
150
+ }
151
+ if (error instanceof TimeoutError || error instanceof NetworkError) {
152
+ if (shouldRetry && attempt < maxRetries) {
153
+ const delay = this.calculateRetryDelay(attempt);
154
+ if (debug) {
155
+ console.log(`[Sovant SDK] Network/Timeout error, retrying after ${delay}ms...`);
156
+ }
157
+ await this.sleep(delay);
158
+ attempt++;
159
+ continue;
160
+ }
161
+ }
162
+ throw error;
163
+ }
164
+ }
165
+ throw lastError || new NetworkError("Max retries exceeded");
166
+ }
167
+ async makeRequest(url, options, timeout, debug) {
168
+ const controller = new AbortController();
169
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
170
+ const headers = {
171
+ "x-sovant-api-key": this.apiKey,
172
+ "Content-Type": "application/json",
173
+ ...options.headers
174
+ };
175
+ const fetchOptions = {
176
+ method: options.method || "GET",
177
+ headers,
178
+ signal: controller.signal
179
+ };
180
+ if (options.body !== void 0 && options.method !== "GET" && options.method !== "HEAD") {
181
+ fetchOptions.body = typeof options.body === "string" ? options.body : JSON.stringify(options.body);
182
+ }
183
+ if (debug) {
184
+ console.log(`[Sovant SDK] ${fetchOptions.method} ${url}`);
185
+ if (options.body) {
186
+ console.log("[Sovant SDK] Body:", options.body);
187
+ }
188
+ }
189
+ try {
190
+ const response = await fetch(url, fetchOptions);
191
+ clearTimeout(timeoutId);
192
+ const responseHeaders = {};
193
+ response.headers.forEach((value, key) => {
194
+ responseHeaders[key.toLowerCase()] = value;
195
+ });
196
+ const requestId = responseHeaders["x-request-id"];
197
+ let data;
198
+ const contentType = responseHeaders["content-type"] || "";
199
+ if (response.status === 204) {
200
+ data = { ok: true };
201
+ } else if (contentType.includes("application/json")) {
202
+ const text = await response.text();
15
203
  try {
16
- const res = await fetch(`${this.baseUrl}${path}`, {
17
- ...init,
18
- headers: {
19
- "content-type": "application/json",
20
- "authorization": `Bearer ${this.apiKey}`,
21
- ...(init.headers || {})
22
- },
23
- signal: ctl.signal
24
- });
25
- const text = await res.text();
26
- let body;
204
+ data = text ? JSON.parse(text) : {};
205
+ } catch (e) {
206
+ data = { message: text };
207
+ }
208
+ } else {
209
+ data = await response.text();
210
+ }
211
+ if (debug) {
212
+ console.log(`[Sovant SDK] Response ${response.status}:`, data);
213
+ if (requestId) {
214
+ console.log(`[Sovant SDK] Request ID: ${requestId}`);
215
+ }
216
+ }
217
+ return {
218
+ data,
219
+ status: response.status,
220
+ headers: responseHeaders,
221
+ requestId
222
+ };
223
+ } catch (error) {
224
+ clearTimeout(timeoutId);
225
+ if (error instanceof Error) {
226
+ if (error.name === "AbortError") {
227
+ throw new TimeoutError(`Request timed out after ${timeout}ms`);
228
+ }
229
+ throw new NetworkError(`Network request failed: ${error.message}`, error);
230
+ }
231
+ throw error;
232
+ }
233
+ }
234
+ calculateRetryDelay(attempt, retryAfter) {
235
+ if (retryAfter) {
236
+ const retryAfterNum = parseInt(retryAfter, 10);
237
+ if (!isNaN(retryAfterNum)) {
238
+ return retryAfterNum * 1e3;
239
+ }
240
+ }
241
+ const baseDelay = Math.min(1e3 * Math.pow(2, attempt), 1e4);
242
+ const jitter = Math.random() * baseDelay * 0.1;
243
+ return Math.floor(baseDelay + jitter);
244
+ }
245
+ sleep(ms) {
246
+ return new Promise((resolve) => setTimeout(resolve, ms));
247
+ }
248
+ };
249
+
250
+ // src/sse-parser.ts
251
+ var SseParser = class {
252
+ constructor(onEvent) {
253
+ this.buffer = "";
254
+ this.onEvent = onEvent;
255
+ }
256
+ feed(chunk) {
257
+ if (!chunk) return;
258
+ this.buffer += chunk;
259
+ this.process();
260
+ }
261
+ process() {
262
+ let boundaryIndex = this.findBoundary(this.buffer);
263
+ while (boundaryIndex !== -1) {
264
+ const raw = this.buffer.slice(0, boundaryIndex);
265
+ this.buffer = this.buffer.slice(boundaryIndex + this.boundaryLength(this.buffer, boundaryIndex));
266
+ const lines = raw.split(/\r?\n/);
267
+ let eventName = "";
268
+ let dataLines = [];
269
+ let id;
270
+ for (const line of lines) {
271
+ if (!line) continue;
272
+ if (line.startsWith(":")) continue;
273
+ const idx = line.indexOf(":");
274
+ const field = idx === -1 ? line : line.slice(0, idx);
275
+ const value = idx === -1 ? "" : line.slice(idx + 1).replace(/^ /, "");
276
+ switch (field) {
277
+ case "event":
278
+ eventName = value;
279
+ break;
280
+ case "data":
281
+ dataLines.push(value);
282
+ break;
283
+ case "id":
284
+ id = value;
285
+ break;
286
+ }
287
+ }
288
+ const data = dataLines.join("\n");
289
+ const ev = { type: "event", event: eventName || "message", data, id };
290
+ this.onEvent(ev);
291
+ if (ev.event === "message" || ev.event === "delta" || ev.event === "") {
292
+ this.onEvent({ type: "delta", data: data ?? "" });
293
+ }
294
+ boundaryIndex = this.findBoundary(this.buffer);
295
+ }
296
+ }
297
+ findBoundary(buf) {
298
+ const n = buf.indexOf("\n\n");
299
+ const r = buf.indexOf("\r\n\r\n");
300
+ if (n === -1) return r;
301
+ if (r === -1) return n;
302
+ return Math.min(n, r);
303
+ }
304
+ boundaryLength(buf, start) {
305
+ if (buf.slice(start, start + 4) === "\r\n\r\n") return 4;
306
+ return 2;
307
+ }
308
+ };
309
+
310
+ // src/index.ts
311
+ var Sovant = class {
312
+ constructor(config) {
313
+ if (!config.apiKey) {
314
+ throw new Error("API key is required");
315
+ }
316
+ const baseUrl = (config.baseUrl || "https://sovant.ai").replace(/\/$/, "");
317
+ this.http = new HttpClient(baseUrl, config.apiKey, {
318
+ timeout: config.timeout || 3e4,
319
+ maxRetries: config.maxRetries || 3,
320
+ debug: config.debug || false
321
+ });
322
+ this.memory = new MemoryNamespace(this.http);
323
+ this.threads = new ThreadsNamespace(this.http);
324
+ this.chat = new ChatNamespace(this.http);
325
+ this.keys = new KeysNamespace(this.http);
326
+ this.recall = new RecallNamespace(this.http);
327
+ }
328
+ };
329
+ var MemoryNamespace = class {
330
+ constructor(http) {
331
+ this.http = http;
332
+ }
333
+ /**
334
+ * Create a new memory
335
+ */
336
+ async create(input) {
337
+ const response = await this.http.request("/api/v1/memory", {
338
+ method: "POST",
339
+ body: input
340
+ });
341
+ const { ok, ...memory } = response.data;
342
+ return memory;
343
+ }
344
+ /**
345
+ * List memories with optional filters
346
+ */
347
+ async list(params) {
348
+ const queryParams = new URLSearchParams();
349
+ if (params?.limit !== void 0) queryParams.append("limit", params.limit.toString());
350
+ if (params?.offset !== void 0) queryParams.append("offset", params.offset.toString());
351
+ if (params?.type) queryParams.append("type", params.type);
352
+ if (params?.thread_id) queryParams.append("thread_id", params.thread_id);
353
+ if (params?.is_archived !== void 0) queryParams.append("is_archived", params.is_archived.toString());
354
+ if (params?.tags && params.tags.length > 0) {
355
+ params.tags.forEach((tag) => queryParams.append("tags", tag));
356
+ }
357
+ const query = queryParams.toString();
358
+ const path = query ? `/api/v1/memory?${query}` : "/api/v1/memory";
359
+ const response = await this.http.request(path, {
360
+ method: "GET"
361
+ });
362
+ return response.data;
363
+ }
364
+ /**
365
+ * Get a specific memory by ID
366
+ */
367
+ async get(id) {
368
+ if (!id) throw new Error("Memory ID is required");
369
+ const response = await this.http.request(`/api/v1/memories/${id}`, {
370
+ method: "GET"
371
+ });
372
+ return response.data;
373
+ }
374
+ /**
375
+ * Update a memory (partial update using PATCH)
376
+ */
377
+ async update(id, patch) {
378
+ if (!id) throw new Error("Memory ID is required");
379
+ const response = await this.http.request(`/api/v1/memories/${id}`, {
380
+ method: "PATCH",
381
+ body: patch
382
+ });
383
+ return response.data;
384
+ }
385
+ /**
386
+ * Replace a memory (full update using PUT)
387
+ */
388
+ async put(id, body) {
389
+ if (!id) throw new Error("Memory ID is required");
390
+ if (!body.content) throw new Error("Content is required for PUT operation");
391
+ if (!body.type) throw new Error("Type is required for PUT operation");
392
+ const response = await this.http.request(`/api/v1/memories/${id}`, {
393
+ method: "PUT",
394
+ body
395
+ });
396
+ return response.data;
397
+ }
398
+ /**
399
+ * Delete a memory
400
+ */
401
+ async delete(id) {
402
+ if (!id) throw new Error("Memory ID is required");
403
+ const response = await this.http.request(`/api/v1/memories/${id}`, {
404
+ method: "DELETE"
405
+ });
406
+ return response.data;
407
+ }
408
+ /**
409
+ * Search memories using semantic search or filters
410
+ */
411
+ async search(params) {
412
+ const queryParams = new URLSearchParams();
413
+ if (params.query) queryParams.append("query", params.query);
414
+ if (params.type) queryParams.append("type", params.type);
415
+ if (params.limit !== void 0) queryParams.append("limit", params.limit.toString());
416
+ if (params.thread_id) queryParams.append("thread_id", params.thread_id);
417
+ if (params.from_date) queryParams.append("from_date", params.from_date);
418
+ if (params.to_date) queryParams.append("to_date", params.to_date);
419
+ if (params.tags && params.tags.length > 0) {
420
+ params.tags.forEach((tag) => queryParams.append("tags", tag));
421
+ }
422
+ const query = queryParams.toString();
423
+ const path = `/api/v1/memory/search${query ? `?${query}` : ""}`;
424
+ const response = await this.http.request(path, {
425
+ method: "GET"
426
+ });
427
+ return response.data;
428
+ }
429
+ /**
430
+ * Execute batch operations atomically
431
+ */
432
+ async batch(body) {
433
+ if (!body.operations || body.operations.length === 0) {
434
+ throw new Error("At least one operation is required");
435
+ }
436
+ const apiBody = {
437
+ operations: body.operations.map((op) => ({
438
+ ...op,
439
+ operation: op.op,
440
+ op: void 0
441
+ }))
442
+ };
443
+ const response = await this.http.request("/api/v1/memory/batch", {
444
+ method: "POST",
445
+ body: apiBody
446
+ });
447
+ return response.data;
448
+ }
449
+ };
450
+ var ThreadsNamespace = class {
451
+ constructor(http) {
452
+ this.http = http;
453
+ }
454
+ /**
455
+ * Create a new thread
456
+ */
457
+ async create(input) {
458
+ const response = await this.http.request("/api/v1/threads", {
459
+ method: "POST",
460
+ body: input || {}
461
+ });
462
+ if (response.data.thread) {
463
+ return response.data.thread;
464
+ } else if (response.data.ok !== void 0) {
465
+ const { ok, ...thread } = response.data;
466
+ return thread;
467
+ } else {
468
+ return response.data;
469
+ }
470
+ }
471
+ /**
472
+ * List threads with pagination
473
+ */
474
+ async list(params) {
475
+ const queryParams = new URLSearchParams();
476
+ if (params?.limit) queryParams.append("limit", params.limit.toString());
477
+ if (params?.offset) queryParams.append("offset", params.offset.toString());
478
+ const path = queryParams.toString() ? `/api/v1/threads?${queryParams.toString()}` : "/api/v1/threads";
479
+ const response = await this.http.request(path, {
480
+ method: "GET"
481
+ });
482
+ return response.data;
483
+ }
484
+ /**
485
+ * Get a thread by ID, optionally with memories
486
+ */
487
+ async get(id, options) {
488
+ const queryParams = new URLSearchParams();
489
+ if (options?.includeMemories) queryParams.append("include", "memories");
490
+ if (options?.limit) queryParams.append("limit", options.limit.toString());
491
+ if (options?.offset) queryParams.append("offset", options.offset.toString());
492
+ const path = queryParams.toString() ? `/api/v1/threads/${id}?${queryParams.toString()}` : `/api/v1/threads/${id}`;
493
+ const response = await this.http.request(path, {
494
+ method: "GET"
495
+ });
496
+ return response.data;
497
+ }
498
+ /**
499
+ * Update a thread
500
+ */
501
+ async update(id, input) {
502
+ const response = await this.http.request(`/api/v1/threads/${id}`, {
503
+ method: "PATCH",
504
+ body: input
505
+ });
506
+ const { ok, ...thread } = response.data;
507
+ return thread;
508
+ }
509
+ /**
510
+ * Delete a thread (memories are unlinked, not deleted)
511
+ */
512
+ async delete(id) {
513
+ const response = await this.http.request(
514
+ `/api/v1/threads/${id}`,
515
+ {
516
+ method: "DELETE"
517
+ }
518
+ );
519
+ return response.data;
520
+ }
521
+ /**
522
+ * Link a memory to a thread
523
+ */
524
+ async linkMemory(threadId, memoryId) {
525
+ const response = await this.http.request(
526
+ `/api/v1/threads/${threadId}/link`,
527
+ {
528
+ method: "POST",
529
+ body: { memory_id: memoryId }
530
+ }
531
+ );
532
+ return response.data;
533
+ }
534
+ };
535
+ var ChatNamespace = class {
536
+ constructor(http) {
537
+ this.http = http;
538
+ }
539
+ /**
540
+ * Create a new chat session
541
+ */
542
+ async createSession(input) {
543
+ const response = await this.http.request("/api/v1/chat/sessions", {
544
+ method: "POST",
545
+ body: input || {}
546
+ });
547
+ return response.data;
548
+ }
549
+ /**
550
+ * Get a chat session by ID
551
+ */
552
+ async getSession(sessionId) {
553
+ const response = await this.http.request(`/api/v1/chat/sessions/${sessionId}`, {
554
+ method: "GET"
555
+ });
556
+ return response.data;
557
+ }
558
+ /**
559
+ * List chat sessions
560
+ */
561
+ async listSessions(params) {
562
+ const queryParams = new URLSearchParams();
563
+ if (params?.limit) queryParams.append("limit", params.limit.toString());
564
+ if (params?.offset) queryParams.append("offset", params.offset.toString());
565
+ const path = queryParams.toString() ? `/api/v1/chat/sessions?${queryParams.toString()}` : "/api/v1/chat/sessions";
566
+ const response = await this.http.request(path, {
567
+ method: "GET"
568
+ });
569
+ return response.data.sessions;
570
+ }
571
+ /**
572
+ * Get messages for a session
573
+ */
574
+ async getMessages(sessionId, limit) {
575
+ const queryParams = new URLSearchParams();
576
+ if (limit) queryParams.append("limit", limit.toString());
577
+ const path = queryParams.toString() ? `/api/v1/chat/sessions/${sessionId}/messages?${queryParams.toString()}` : `/api/v1/chat/sessions/${sessionId}/messages`;
578
+ const response = await this.http.request(path, {
579
+ method: "GET"
580
+ });
581
+ return response.data.messages;
582
+ }
583
+ /**
584
+ * Send a message with SSE streaming
585
+ */
586
+ async *sendMessage(sessionId, message, opts) {
587
+ const options = typeof message === "string" ? opts : message;
588
+ const messageText = typeof message === "string" ? message : message.message;
589
+ if (!messageText) {
590
+ yield { type: "error", data: "Message is required" };
591
+ return;
592
+ }
593
+ const body = {
594
+ message: messageText,
595
+ provider: options?.provider,
596
+ model: options?.model,
597
+ useMemory: options?.useMemory ?? true,
598
+ stream: options?.stream ?? true
599
+ };
600
+ const response = await fetch(`${this.http.baseUrl}/api/v1/chat/sessions/${sessionId}/messages`, {
601
+ method: "POST",
602
+ headers: {
603
+ "x-sovant-api-key": this.http.apiKey,
604
+ "content-type": "application/json",
605
+ "accept": "text/event-stream"
606
+ },
607
+ body: JSON.stringify(body),
608
+ signal: options?.signal
609
+ });
610
+ if (!response.ok) {
611
+ const error = await response.json().catch(() => ({ message: "Request failed" }));
612
+ yield { type: "error", data: error.message || "Request failed" };
613
+ return;
614
+ }
615
+ const reader = response.body?.getReader();
616
+ if (!reader) {
617
+ yield { type: "error", data: "No response body" };
618
+ return;
619
+ }
620
+ const decoder = new TextDecoder();
621
+ const queue = [];
622
+ const parser = new SseParser((ev) => {
623
+ queue.push(ev);
624
+ });
625
+ try {
626
+ while (true) {
627
+ const { done, value } = await reader.read();
628
+ if (done) break;
629
+ const text = decoder.decode(value, { stream: true });
630
+ parser.feed(text);
631
+ while (queue.length > 0) {
632
+ const ev = queue.shift();
633
+ if (ev.type === "delta") {
27
634
  try {
28
- body = text ? JSON.parse(text) : undefined;
29
- }
30
- catch {
31
- body = text;
635
+ const parsed = JSON.parse(ev.data);
636
+ if (parsed.text !== void 0) {
637
+ yield { type: "delta", data: parsed.text };
638
+ } else if (parsed.data !== void 0) {
639
+ yield { type: "delta", data: parsed.data };
640
+ } else {
641
+ yield { type: "delta", data: ev.data };
642
+ }
643
+ } catch {
644
+ yield { type: "delta", data: ev.data };
32
645
  }
33
- if (!res.ok) {
34
- const msg = body?.message || res.statusText;
35
- const code = body?.code || `HTTP_${res.status}`;
36
- throw new SovantError(msg, code, res.status, body);
646
+ } else if (ev.type === "event") {
647
+ if (ev.event === "done") {
648
+ yield { type: "done" };
649
+ } else if (ev.event === "error") {
650
+ yield { type: "error", data: ev.data };
651
+ } else if (ev.event === "meta" || ev.event === "provider") {
652
+ continue;
37
653
  }
38
- return body;
654
+ }
39
655
  }
40
- finally {
41
- clearTimeout(id);
656
+ }
657
+ yield { type: "done" };
658
+ } catch (error) {
659
+ if (error.name === "AbortError") {
660
+ yield { type: "error", data: "Request cancelled" };
661
+ } else {
662
+ yield { type: "error", data: error.message };
663
+ }
664
+ } finally {
665
+ reader.releaseLock();
666
+ }
667
+ }
668
+ };
669
+ var KeysNamespace = class {
670
+ constructor(http) {
671
+ this.http = http;
672
+ }
673
+ /**
674
+ * List API keys
675
+ */
676
+ async list() {
677
+ const response = await this.http.request("/api/v1/keys", {
678
+ method: "GET"
679
+ });
680
+ return response.data.keys;
681
+ }
682
+ /**
683
+ * Create a new API key
684
+ */
685
+ async create(name) {
686
+ const response = await this.http.request("/api/v1/keys", {
687
+ method: "POST",
688
+ body: { name }
689
+ });
690
+ return response.data;
691
+ }
692
+ /**
693
+ * Update an API key
694
+ */
695
+ async update(id, patch) {
696
+ const response = await this.http.request("/api/v1/keys", {
697
+ method: "PATCH",
698
+ body: { id, ...patch }
699
+ });
700
+ return response.data;
701
+ }
702
+ /**
703
+ * Revoke an API key
704
+ */
705
+ async revoke(id) {
706
+ await this.http.request(`/api/v1/keys/${id}`, {
707
+ method: "DELETE"
708
+ });
709
+ }
710
+ };
711
+ var RecallNamespace = class {
712
+ constructor(http) {
713
+ this.http = http;
714
+ }
715
+ /**
716
+ * Extract profile entity from text (client-side)
717
+ */
718
+ extractProfile(text) {
719
+ const s = (text || "").toLowerCase().trim();
720
+ const sanitized = s.replace(/'/g, "'");
721
+ if (/\bmy\s+name\s+is\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i.test(sanitized)) {
722
+ const match = sanitized.match(/\bmy\s+name\s+is\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i);
723
+ if (match) {
724
+ const name = match[1].trim().replace(/\s+/g, " ").replace(/\.$/, "");
725
+ return { entity: "name", value: name };
726
+ }
727
+ }
728
+ if (/\b(i am|i'm|im)\s+(\d{1,3})\s*(years old)?/i.test(sanitized)) {
729
+ const match = sanitized.match(/\b(i am|i'm|im)\s+(\d{1,3})/i);
730
+ if (match) {
731
+ return { entity: "age", value: match[2] };
732
+ }
733
+ }
734
+ if (/\bmy\s+age\s+is\s+(\d{1,3})/i.test(sanitized)) {
735
+ const match = sanitized.match(/\bmy\s+age\s+is\s+(\d{1,3})/i);
736
+ if (match) {
737
+ return { entity: "age", value: match[1] };
738
+ }
739
+ }
740
+ const locationPatterns = [
741
+ /\bi\s+live\s+in\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i,
742
+ /\bi\s+am\s+based\s+in\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i,
743
+ /\bi'm\s+from\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i,
744
+ /\bi\s+am\s+from\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i,
745
+ /\bresiding\s+in\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i,
746
+ /\bcurrently\s+in\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i,
747
+ /\bmoved\s+to\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i,
748
+ /\blocated\s+in\s+([a-z\-.''\s]{2,}?)(?:[.!?\s]|$)/i
749
+ ];
750
+ for (const pattern of locationPatterns) {
751
+ if (pattern.test(sanitized)) {
752
+ const match = sanitized.match(pattern);
753
+ if (match) {
754
+ const location = match[1].trim().replace(/\s+/g, " ").replace(/\.$/, "");
755
+ return { entity: "location", value: location };
42
756
  }
757
+ }
758
+ }
759
+ if (/\bi\s+(like|love|prefer)\s+([^.!?]{2,})/i.test(sanitized)) {
760
+ const match = sanitized.match(/\bi\s+(like|love|prefer)\s+([^.!?]{2,})/i);
761
+ if (match) {
762
+ const pref = match[2].trim().replace(/\s+/g, " ").replace(/\.$/, "");
763
+ return { entity: "preference", value: pref };
764
+ }
43
765
  }
44
- memory = {
45
- create: (input) => this.req("/api/v1/memory", {
46
- method: "POST",
47
- body: JSON.stringify({
48
- content: typeof input.data === 'string' ? input.data : JSON.stringify(input.data),
49
- type: input.type || 'journal',
50
- ttl: input.ttl,
51
- tags: input.tags,
52
- metadata: input.metadata,
53
- thread_id: input.thread_id
54
- })
55
- }),
56
- get: (id) => this.req(`/api/v1/memories/${encodeURIComponent(id)}`, {
57
- method: "GET"
58
- }),
59
- search: (q) => {
60
- const params = new URLSearchParams();
61
- if (q.query)
62
- params.append('query', q.query);
63
- if (q.type)
64
- params.append('type', q.type);
65
- if (q.tags)
66
- params.append('tags', q.tags.join(','));
67
- if (q.thread_id)
68
- params.append('thread_id', q.thread_id);
69
- if (q.limit)
70
- params.append('limit', q.limit.toString());
71
- if (q.from_date)
72
- params.append('from_date', q.from_date);
73
- if (q.to_date)
74
- params.append('to_date', q.to_date);
75
- return this.req(`/api/v1/memory/search?${params.toString()}`, {
76
- method: "GET"
77
- });
78
- },
79
- update: (id, patch) => this.req(`/api/v1/memories/${encodeURIComponent(id)}`, {
80
- method: "PATCH",
81
- body: JSON.stringify({
82
- content: patch.data ? (typeof patch.data === 'string' ? patch.data : JSON.stringify(patch.data)) : undefined,
83
- type: patch.type,
84
- ttl: patch.ttl,
85
- tags: patch.tags,
86
- metadata: patch.metadata
87
- })
88
- }),
89
- delete: (id) => this.req(`/api/v1/memories/${encodeURIComponent(id)}`, {
90
- method: "DELETE"
91
- })
766
+ return { entity: null };
767
+ }
768
+ /**
769
+ * Get profile facts from memories
770
+ */
771
+ async getProfileFacts() {
772
+ const response = await this.http.request(
773
+ "/api/v1/memory/search?query=user%20name%20age%20location&limit=20",
774
+ { method: "GET" }
775
+ );
776
+ const facts = {
777
+ preferences: []
92
778
  };
93
- }
94
- export class SovantError extends Error {
95
- code;
96
- status;
97
- details;
98
- constructor(message, code, status, details) {
99
- super(message);
100
- this.name = "SovantError";
101
- this.code = code;
102
- this.status = status;
103
- this.details = details;
104
- }
105
- }
106
- //# sourceMappingURL=index.js.map
779
+ for (const memory of response.data.results) {
780
+ const content = memory.content.toLowerCase();
781
+ if (/^user\s+name\s+is\s+(.+)$/i.test(content)) {
782
+ const match = content.match(/^user\s+name\s+is\s+(.+)$/i);
783
+ if (match && !facts.name) {
784
+ facts.name = match[1].trim();
785
+ }
786
+ }
787
+ if (/^user\s+age\s+is\s+(\d+)$/i.test(content)) {
788
+ const match = content.match(/^user\s+age\s+is\s+(\d+)$/i);
789
+ if (match && !facts.age) {
790
+ facts.age = match[1];
791
+ }
792
+ }
793
+ if (/^user\s+location\s+is\s+(.+)$/i.test(content)) {
794
+ const match = content.match(/^user\s+location\s+is\s+(.+)$/i);
795
+ if (match && !facts.location) {
796
+ facts.location = match[1].trim();
797
+ }
798
+ }
799
+ if (/^user\s+(likes|loves|prefers)\s+(.+)$/i.test(content)) {
800
+ const match = content.match(/^user\s+(likes|loves|prefers)\s+(.+)$/i);
801
+ if (match) {
802
+ const pref = match[2].trim();
803
+ if (!facts.preferences?.includes(pref)) {
804
+ facts.preferences?.push(pref);
805
+ }
806
+ }
807
+ }
808
+ }
809
+ return facts;
810
+ }
811
+ /**
812
+ * Save a profile fact in canonical form
813
+ */
814
+ async saveProfileFact(kind, value) {
815
+ let content;
816
+ switch (kind) {
817
+ case "name":
818
+ content = `user name is ${value}`;
819
+ break;
820
+ case "age":
821
+ content = `user age is ${value}`;
822
+ break;
823
+ case "location":
824
+ content = `user location is ${value}`;
825
+ break;
826
+ case "preference":
827
+ content = `user likes ${value}`;
828
+ break;
829
+ default:
830
+ throw new Error(`Unknown profile entity: ${kind}`);
831
+ }
832
+ const response = await this.http.request("/api/v1/memory", {
833
+ method: "POST",
834
+ body: {
835
+ content,
836
+ type: "preference",
837
+ tags: ["profile", kind]
838
+ }
839
+ });
840
+ const { ok, ...memory } = response.data;
841
+ return memory;
842
+ }
843
+ };
844
+ var index_default = Sovant;
845
+ // Annotate the CommonJS export names for ESM import in node:
846
+ 0 && (module.exports = {
847
+ NetworkError,
848
+ Sovant,
849
+ SovantError,
850
+ TimeoutError
851
+ });