@wiscale/velesdb-sdk 1.18.0 → 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.
package/dist/index.js CHANGED
@@ -5,8 +5,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __esm = (fn, res) => function __init() {
9
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ var __esm = (fn, res, err) => function __init() {
9
+ if (err) throw err[0];
10
+ try {
11
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
12
+ } catch (e) {
13
+ throw err = [e], e;
14
+ }
10
15
  };
11
16
  var __export = (target, all) => {
12
17
  for (var name in all)
@@ -182,6 +187,7 @@ var REST_CAPABILITIES = Object.freeze({
182
187
  graphTraversal: true,
183
188
  secondaryIndexes: true,
184
189
  agentMemory: true,
190
+ enableStreaming: true,
185
191
  streamInsert: true,
186
192
  pqTraining: true,
187
193
  velesqlQuery: true,
@@ -197,9 +203,10 @@ var WASM_CAPABILITIES = Object.freeze({
197
203
  graphTraversal: false,
198
204
  secondaryIndexes: false,
199
205
  agentMemory: false,
206
+ enableStreaming: false,
200
207
  streamInsert: false,
201
208
  pqTraining: false,
202
- velesqlQuery: true,
209
+ velesqlQuery: false,
203
210
  collectionIntrospection: false
204
211
  });
205
212
 
@@ -437,20 +444,47 @@ async function wasmMultiQuerySearch(ctx, collectionName, vectors, options) {
437
444
  );
438
445
  return raw.map((r) => mapWasmResult(ctx, collection, r));
439
446
  }
440
- async function wasmQuery(ctx, collectionName, _queryString, params, _options) {
447
+ var PURE_NEAR_QUERY = /^\s*select\s+\*\s+from\s+([a-z_]\w*)\s+where\s+vector\s+near\s+\$([a-z_]\w*)\s*(?:limit\s+(\d+))?\s*;?\s*$/i;
448
+ function parsePureNearQuery(queryString) {
449
+ const match = PURE_NEAR_QUERY.exec(queryString);
450
+ if (!match) {
451
+ throw new VelesDBError(
452
+ `The WASM backend only executes pure top-k NEAR queries of the form "SELECT * FROM <collection> WHERE vector NEAR $param [LIMIT n]". WHERE predicates, JOIN, GROUP BY, MATCH, set operations and FUSION are not evaluated in WASM \u2014 use the REST backend (velesdb-server) for full VelesQL. Received: ${queryString}`,
453
+ "NOT_SUPPORTED"
454
+ );
455
+ }
456
+ const parsed = { from: match[1], param: match[2] };
457
+ if (match[3] !== void 0) {
458
+ parsed.limit = Number(match[3]);
459
+ }
460
+ return parsed;
461
+ }
462
+ function resolveQueryK(limit, requestedK) {
463
+ if (limit !== void 0) {
464
+ return limit;
465
+ }
466
+ return typeof requestedK === "number" && Number.isInteger(requestedK) && requestedK > 0 ? requestedK : 10;
467
+ }
468
+ async function wasmQuery(ctx, collectionName, queryString, params, _options) {
441
469
  const collection = ctx.getCollection(collectionName);
442
470
  if (!collection) {
443
471
  throw new NotFoundError(`Collection '${collectionName}'`);
444
472
  }
445
- const paramsVector = params?.q;
473
+ const parsed = parsePureNearQuery(queryString);
474
+ if (parsed.from !== collectionName) {
475
+ throw new VelesDBError(
476
+ `Query targets collection '${parsed.from}' but was executed against '${collectionName}'.`,
477
+ "BAD_REQUEST"
478
+ );
479
+ }
480
+ const paramsVector = params?.[parsed.param];
446
481
  if (!Array.isArray(paramsVector) && !(paramsVector instanceof Float32Array)) {
447
482
  throw new VelesDBError(
448
- "WASM query() expects params.q to contain the query embedding vector.",
483
+ `WASM query() expects params.${parsed.param} to contain the query embedding vector.`,
449
484
  "BAD_REQUEST"
450
485
  );
451
486
  }
452
- const requestedK = params?.k;
453
- const k = typeof requestedK === "number" && Number.isInteger(requestedK) && requestedK > 0 ? requestedK : 10;
487
+ const k = resolveQueryK(parsed.limit, params?.k);
454
488
  const raw = collection.store.query(
455
489
  paramsVector instanceof Float32Array ? paramsVector : new Float32Array(paramsVector),
456
490
  k
@@ -864,6 +898,9 @@ function validateCollectionName(name) {
864
898
  );
865
899
  }
866
900
  }
901
+ async function safeJsonParse(response) {
902
+ return response.json().catch(() => ({}));
903
+ }
867
904
  function toNumberArray(v) {
868
905
  return v instanceof Float32Array ? Array.from(v) : v;
869
906
  }
@@ -917,12 +954,18 @@ async function wasmScroll(_collection, _request) {
917
954
  async function wasmTrainPq(_collection, _options) {
918
955
  wasmNotSupported("PQ training");
919
956
  }
957
+ async function wasmEnableStreaming(_collection, _config) {
958
+ wasmNotSupported("Streaming enable");
959
+ }
920
960
  async function wasmStreamInsert(_collection, _docs) {
921
961
  wasmNotSupported("Streaming insert");
922
962
  }
923
963
  async function wasmStreamUpsertPoints(_collection, _docs) {
924
964
  wasmNotSupported("Streaming batch upsert");
925
965
  }
966
+ async function wasmUpsertBatchRaw(_collection, _docs) {
967
+ wasmNotSupported("Binary bulk upsert (upsertBatchRaw)");
968
+ }
926
969
  async function wasmCreateGraphCollection(_name, _config) {
927
970
  wasmNotSupported("Graph collections");
928
971
  }
@@ -938,6 +981,9 @@ async function wasmGetCollectionConfig(_collection) {
938
981
  async function wasmSearchIds(_collection, _query, _options) {
939
982
  wasmNotSupported("searchIds");
940
983
  }
984
+ async function wasmMultiQuerySearchIds(_collection, _vectors, _options) {
985
+ wasmNotSupported("multiQuerySearchIds");
986
+ }
941
987
  async function wasmStoreSemanticFact(_collection, _entry) {
942
988
  wasmNotSupported("Agent memory");
943
989
  }
@@ -950,6 +996,12 @@ async function wasmRecordEpisodicEvent(_collection, _event) {
950
996
  async function wasmRecallEpisodicEvents(_collection, _embedding, _k) {
951
997
  wasmNotSupported("Agent memory");
952
998
  }
999
+ async function wasmRecallRecentEvents(_collection, _since) {
1000
+ wasmNotSupported("Agent memory");
1001
+ }
1002
+ async function wasmRecallOlderThanEvents(_collection, _before) {
1003
+ wasmNotSupported("Agent memory");
1004
+ }
953
1005
  async function wasmStoreProceduralPattern(_collection, _pattern) {
954
1006
  wasmNotSupported("Agent memory");
955
1007
  }
@@ -997,6 +1049,18 @@ function wasmUpsertNodePayload(_c, _id, _p) {
997
1049
  function wasmGraphSearch(_c, _r) {
998
1050
  return Promise.resolve(wasmNotSupported("Graph search"));
999
1051
  }
1052
+ function wasmRelate(_c, _req) {
1053
+ return Promise.resolve(wasmNotSupported("Relation edges"));
1054
+ }
1055
+ function wasmUnrelate(_c, _id) {
1056
+ return Promise.resolve(wasmNotSupported("Relation edge removal"));
1057
+ }
1058
+ function wasmGetRelations(_c, _id) {
1059
+ return Promise.resolve(wasmNotSupported("Relation edges"));
1060
+ }
1061
+ function wasmSetTtlDurable(_c, _id, _ttl) {
1062
+ return Promise.resolve(wasmNotSupported("Durable TTL"));
1063
+ }
1000
1064
 
1001
1065
  // src/backends/wasm.ts
1002
1066
  var WasmBackend = class {
@@ -1176,6 +1240,9 @@ var WasmBackend = class {
1176
1240
  }
1177
1241
  }
1178
1242
  }
1243
+ async upsertBatchRaw(c, d) {
1244
+ return wasmUpsertBatchRaw(c, d);
1245
+ }
1179
1246
  async delete(collectionName, id) {
1180
1247
  this.ensureInitialized();
1181
1248
  const collection = this.collections.get(collectionName);
@@ -1307,6 +1374,10 @@ var WasmBackend = class {
1307
1374
  this.ensureInitialized();
1308
1375
  return wasmTrainPq(c, o);
1309
1376
  }
1377
+ async enableStreaming(c, cfg) {
1378
+ this.ensureInitialized();
1379
+ return wasmEnableStreaming(c, cfg);
1380
+ }
1310
1381
  async streamInsert(c, d) {
1311
1382
  this.ensureInitialized();
1312
1383
  return wasmStreamInsert(c, d);
@@ -1335,6 +1406,10 @@ var WasmBackend = class {
1335
1406
  this.ensureInitialized();
1336
1407
  return wasmSearchIds(c, q, o);
1337
1408
  }
1409
+ async multiQuerySearchIds(c, v, o) {
1410
+ this.ensureInitialized();
1411
+ return wasmMultiQuerySearchIds(c, v, o);
1412
+ }
1338
1413
  async storeSemanticFact(c, e) {
1339
1414
  this.ensureInitialized();
1340
1415
  return wasmStoreSemanticFact(c, e);
@@ -1351,6 +1426,14 @@ var WasmBackend = class {
1351
1426
  this.ensureInitialized();
1352
1427
  return wasmRecallEpisodicEvents(c, e, k);
1353
1428
  }
1429
+ async recallRecentEvents(c, since) {
1430
+ this.ensureInitialized();
1431
+ return wasmRecallRecentEvents(c, since);
1432
+ }
1433
+ async recallOlderThanEvents(c, before) {
1434
+ this.ensureInitialized();
1435
+ return wasmRecallOlderThanEvents(c, before);
1436
+ }
1354
1437
  async storeProceduralPattern(c, p) {
1355
1438
  this.ensureInitialized();
1356
1439
  return wasmStoreProceduralPattern(c, p);
@@ -1412,16 +1495,46 @@ var WasmBackend = class {
1412
1495
  this.ensureInitialized();
1413
1496
  return wasmSparseSearchNamed(c, q, idx, o);
1414
1497
  }
1498
+ async relate(c, req) {
1499
+ this.ensureInitialized();
1500
+ return wasmRelate(c, req);
1501
+ }
1502
+ async unrelate(c, edgeId) {
1503
+ this.ensureInitialized();
1504
+ return wasmUnrelate(c, edgeId);
1505
+ }
1506
+ async getRelations(c, pointId) {
1507
+ this.ensureInitialized();
1508
+ return wasmGetRelations(c, pointId);
1509
+ }
1510
+ async setTtlDurable(c, pointId, ttlSeconds) {
1511
+ this.ensureInitialized();
1512
+ return wasmSetTtlDurable(c, pointId, ttlSeconds);
1513
+ }
1415
1514
  };
1416
1515
 
1417
1516
  // src/backends/crud-backend.ts
1517
+ var RAW_BULK_HEADER_LEN = 16;
1518
+ var RAW_BULK_MAGIC = [86, 82, 66, 49];
1519
+ var RAW_BULK_ID_WIDTH = 8;
1520
+ var U64_MAX = 18446744073709551615n;
1521
+ function coerceDecimalStringId(id) {
1522
+ if (!/^\d+$/.test(id)) return NaN;
1523
+ const big = BigInt(id);
1524
+ if (big > U64_MAX) return NaN;
1525
+ return big > BigInt(Number.MAX_SAFE_INTEGER) ? id : Number(id);
1526
+ }
1418
1527
  function parseRestPointId(id) {
1419
- if (typeof id !== "number" || !Number.isFinite(id) || id < 0 || !Number.isInteger(id) || id > Number.MAX_SAFE_INTEGER) {
1528
+ const coerced = typeof id === "string" ? coerceDecimalStringId(id) : id;
1529
+ if (typeof coerced === "string") {
1530
+ return coerced;
1531
+ }
1532
+ if (!Number.isFinite(coerced) || coerced < 0 || !Number.isInteger(coerced) || coerced > Number.MAX_SAFE_INTEGER) {
1420
1533
  throw new ValidationError(
1421
- `REST backend requires numeric u64-compatible IDs in JS safe integer range (0..${Number.MAX_SAFE_INTEGER}). Received: ${String(id)}`
1534
+ `REST backend requires numeric u64-compatible IDs: a non-negative integer in the JS safe integer range (0..${Number.MAX_SAFE_INTEGER}) or a decimal string up to u64::MAX (${U64_MAX}). Received: ${String(id)}`
1422
1535
  );
1423
1536
  }
1424
- return id;
1537
+ return coerced;
1425
1538
  }
1426
1539
  function sparseVectorToRestFormat(sv) {
1427
1540
  const result = {};
@@ -1554,6 +1667,105 @@ async function flush(transport, collection) {
1554
1667
  );
1555
1668
  throwOnError(response, `Collection '${collection}'`);
1556
1669
  }
1670
+ function encodeRawBulk(ids, vectors, dim) {
1671
+ const count = ids.length;
1672
+ if (vectors.length !== count) {
1673
+ throw new ValidationError(
1674
+ `encodeRawBulk: ids length (${count}) must match vectors length (${vectors.length})`
1675
+ );
1676
+ }
1677
+ const buf = new Uint8Array(RAW_BULK_HEADER_LEN + count * 8 + count * dim * 4);
1678
+ const view = new DataView(buf.buffer);
1679
+ buf.set(RAW_BULK_MAGIC, 0);
1680
+ view.setUint32(4, count, true);
1681
+ view.setUint32(8, dim, true);
1682
+ buf[12] = RAW_BULK_ID_WIDTH;
1683
+ writeIds(view, ids);
1684
+ writeVectors(view, vectors, dim, count);
1685
+ return buf;
1686
+ }
1687
+ function writeIds(view, ids) {
1688
+ let off = RAW_BULK_HEADER_LEN;
1689
+ for (const id of ids) {
1690
+ view.setBigUint64(off, BigInt(id), true);
1691
+ off += 8;
1692
+ }
1693
+ }
1694
+ function writeVectors(view, vectors, dim, count) {
1695
+ let off = RAW_BULK_HEADER_LEN + count * 8;
1696
+ for (const vec of vectors) {
1697
+ if (vec.length !== dim) {
1698
+ throw new ValidationError(
1699
+ `encodeRawBulk: vector length (${vec.length}) must match dim (${dim})`
1700
+ );
1701
+ }
1702
+ for (let i = 0; i < dim; i++) {
1703
+ view.setFloat32(off, vec[i] ?? 0, true);
1704
+ off += 4;
1705
+ }
1706
+ }
1707
+ }
1708
+ async function upsertBatchRaw(transport, collection, docs, dim) {
1709
+ const ids = docs.map((d) => coerceNumericId(d.id));
1710
+ const vectors = docs.map((d) => d.vector);
1711
+ const body = encodeRawBulk(ids, vectors, dim);
1712
+ return sendRawBulk(transport, collection, body);
1713
+ }
1714
+ function coerceNumericId(id) {
1715
+ const parsed = parseRestPointId(id);
1716
+ if (typeof parsed === "string") {
1717
+ throw new ValidationError(
1718
+ `upsertBatchRaw requires ids in the JS safe integer range; received: ${parsed}`
1719
+ );
1720
+ }
1721
+ return parsed;
1722
+ }
1723
+ async function sendRawBulk(transport, collection, body) {
1724
+ const url = `${transport.baseUrl}${collectionPath(collection)}/points/raw`;
1725
+ const headers = {
1726
+ "Content-Type": "application/octet-stream"
1727
+ };
1728
+ if (transport.apiKey) {
1729
+ headers["Authorization"] = `Bearer ${transport.apiKey}`;
1730
+ }
1731
+ const controller = new AbortController();
1732
+ const timeoutId = setTimeout(() => controller.abort(), transport.timeout);
1733
+ try {
1734
+ const response = await fetch(url, {
1735
+ method: "POST",
1736
+ headers,
1737
+ body,
1738
+ signal: controller.signal
1739
+ });
1740
+ clearTimeout(timeoutId);
1741
+ return await parseRawBulkResponse(response);
1742
+ } catch (error) {
1743
+ clearTimeout(timeoutId);
1744
+ throw wrapRawBulkError(error);
1745
+ }
1746
+ }
1747
+ async function parseRawBulkResponse(response) {
1748
+ const data = await safeJsonParse(response);
1749
+ if (!response.ok) {
1750
+ const code = typeof data.code === "string" ? data.code : `HTTP_${response.status}`;
1751
+ const message = typeof data.error === "string" ? data.error : `HTTP ${response.status}`;
1752
+ throw new VelesDBError(message, code);
1753
+ }
1754
+ return typeof data.count === "number" ? data.count : 0;
1755
+ }
1756
+ function wrapRawBulkError(error) {
1757
+ if (error instanceof VelesDBError) {
1758
+ return error;
1759
+ }
1760
+ if (error instanceof Error && error.name === "AbortError") {
1761
+ return new ConnectionError("Request timeout");
1762
+ }
1763
+ const message = error instanceof Error ? error.message : "Unknown error";
1764
+ return new ConnectionError(
1765
+ `Raw bulk upsert failed: ${message}`,
1766
+ error instanceof Error ? error : void 0
1767
+ );
1768
+ }
1557
1769
 
1558
1770
  // src/backends/rest-http.ts
1559
1771
  var STATUS_ERROR_CODES = {
@@ -1624,7 +1836,7 @@ async function request(config, method, path, body) {
1624
1836
  signal: controller.signal
1625
1837
  });
1626
1838
  clearTimeout(timeoutId);
1627
- const data = await response.json().catch(() => ({}));
1839
+ const data = await safeJsonParse(response);
1628
1840
  if (!response.ok) {
1629
1841
  const ep = extractErrorPayload(data);
1630
1842
  return { error: {
@@ -1648,6 +1860,13 @@ function buildCrudTransport(config) {
1648
1860
  requestJson: (m, p, b) => request(config, m, p, b)
1649
1861
  };
1650
1862
  }
1863
+ function buildRawBulkTransport(config) {
1864
+ return {
1865
+ baseUrl: config.baseUrl,
1866
+ apiKey: config.apiKey,
1867
+ timeout: config.timeout
1868
+ };
1869
+ }
1651
1870
  function buildSearchTransport(config) {
1652
1871
  return {
1653
1872
  requestJson: (m, p, b) => request(config, m, p, b),
@@ -1876,6 +2095,31 @@ async function graphSearch(transport, collection, request2) {
1876
2095
  return { results: items };
1877
2096
  }
1878
2097
 
2098
+ // src/backends/scroll-backend.ts
2099
+ async function scroll(transport, collection, request2) {
2100
+ const body = {};
2101
+ if (request2?.cursor !== void 0) {
2102
+ body.cursor = request2.cursor;
2103
+ }
2104
+ if (request2?.batchSize !== void 0) {
2105
+ body.batch_size = request2.batchSize;
2106
+ }
2107
+ if (request2?.filter !== void 0) {
2108
+ body.filter = request2.filter;
2109
+ }
2110
+ const response = await transport.requestJson(
2111
+ "POST",
2112
+ `${collectionPath(collection)}/points/scroll`,
2113
+ body
2114
+ );
2115
+ throwOnError(response, `Collection '${collection}'`);
2116
+ const data = response.data;
2117
+ return {
2118
+ points: data.points,
2119
+ nextCursor: data.next_cursor
2120
+ };
2121
+ }
2122
+
1879
2123
  // src/backends/agent-memory-backend.ts
1880
2124
  var _idCounter = 0;
1881
2125
  var _lastTimestamp = 0;
@@ -1893,20 +2137,28 @@ function generateUniqueId() {
1893
2137
  }
1894
2138
  return _lastTimestamp * 1e3 + _idCounter;
1895
2139
  }
2140
+ function memoryIdToString(id) {
2141
+ return String(id);
2142
+ }
2143
+ function nowUnixSeconds() {
2144
+ return Math.floor(Date.now() / 1e3);
2145
+ }
1896
2146
  async function storeSemanticFact(transport, collection, entry) {
1897
2147
  const response = await transport.requestJson(
1898
2148
  "POST",
1899
2149
  `${collectionPath(collection)}/points`,
1900
2150
  {
1901
2151
  points: [{
1902
- id: entry.id,
2152
+ id: parseRestPointId(entry.id),
1903
2153
  vector: entry.embedding,
1904
2154
  payload: {
1905
2155
  // Caller metadata is spread first so the reserved keys below
1906
- // (`_memory_type`, `text`) always win and cannot be clobbered.
2156
+ // (`_memory_type`, `content`) always win and cannot be clobbered.
1907
2157
  ...entry.metadata,
1908
2158
  _memory_type: "semantic",
1909
- text: entry.text
2159
+ // `content` matches the core semantic store and the server/Python
2160
+ // payload field (BREAKING: was `text` before this change).
2161
+ content: entry.text
1910
2162
  }
1911
2163
  }]
1912
2164
  }
@@ -1917,7 +2169,8 @@ async function searchSemanticMemory(transport, collection, embedding, k = 5) {
1917
2169
  return transport.searchVectors(collection, embedding, k, { _memory_type: "semantic" });
1918
2170
  }
1919
2171
  async function recordEpisodicEvent(transport, collection, event) {
1920
- const id = generateUniqueId();
2172
+ const id = event.id !== void 0 ? parseRestPointId(event.id) : generateUniqueId();
2173
+ const timestamp = event.timestamp ?? nowUnixSeconds();
1921
2174
  const response = await transport.requestJson(
1922
2175
  "POST",
1923
2176
  `${collectionPath(collection)}/points`,
@@ -1933,19 +2186,21 @@ async function recordEpisodicEvent(transport, collection, event) {
1933
2186
  ...event.metadata,
1934
2187
  _memory_type: "episodic",
1935
2188
  event_type: event.eventType,
1936
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
2189
+ // NUMERIC unix-seconds, mirroring the core episodic store so
2190
+ // recallRecent/recallOlderThan can range-filter on it.
2191
+ timestamp
1937
2192
  }
1938
2193
  }]
1939
2194
  }
1940
2195
  );
1941
2196
  throwOnError(response);
1942
- return id;
2197
+ return memoryIdToString(id);
1943
2198
  }
1944
2199
  async function recallEpisodicEvents(transport, collection, embedding, k = 5) {
1945
2200
  return transport.searchVectors(collection, embedding, k, { _memory_type: "episodic" });
1946
2201
  }
1947
2202
  async function storeProceduralPattern(transport, collection, pattern) {
1948
- const id = generateUniqueId();
2203
+ const id = pattern.id !== void 0 ? parseRestPointId(pattern.id) : generateUniqueId();
1949
2204
  const response = await transport.requestJson(
1950
2205
  "POST",
1951
2206
  `${collectionPath(collection)}/points`,
@@ -1966,11 +2221,41 @@ async function storeProceduralPattern(transport, collection, pattern) {
1966
2221
  }
1967
2222
  );
1968
2223
  throwOnError(response);
1969
- return id;
2224
+ return memoryIdToString(id);
1970
2225
  }
1971
2226
  async function matchProceduralPatterns(transport, collection, embedding, k = 5) {
1972
2227
  return transport.searchVectors(collection, embedding, k, { _memory_type: "procedural" });
1973
2228
  }
2229
+ function toEpisodicRecord(point) {
2230
+ const payload = point.payload ?? {};
2231
+ if (payload._memory_type !== "episodic") return void 0;
2232
+ if (typeof payload.timestamp !== "number") return void 0;
2233
+ return { id: String(point.id), timestamp: payload.timestamp, payload };
2234
+ }
2235
+ async function scrollEpisodicRecords(transport, collection) {
2236
+ const records = [];
2237
+ let cursor = null;
2238
+ do {
2239
+ const page = await scroll(transport, collection, {
2240
+ cursor: cursor ?? void 0,
2241
+ filter: { _memory_type: "episodic" }
2242
+ });
2243
+ for (const point of page.points) {
2244
+ const record = toEpisodicRecord(point);
2245
+ if (record !== void 0) records.push(record);
2246
+ }
2247
+ cursor = page.nextCursor;
2248
+ } while (cursor !== null && cursor !== void 0);
2249
+ return records;
2250
+ }
2251
+ async function recallRecentEvents(transport, collection, since) {
2252
+ const records = await scrollEpisodicRecords(transport, collection);
2253
+ return records.filter((r) => since === void 0 || r.timestamp >= since).sort((a, b) => b.timestamp - a.timestamp);
2254
+ }
2255
+ async function recallOlderThanEvents(transport, collection, before) {
2256
+ const records = await scrollEpisodicRecords(transport, collection);
2257
+ return records.filter((r) => r.timestamp < before).sort((a, b) => b.timestamp - a.timestamp);
2258
+ }
1974
2259
 
1975
2260
  // src/search-quality.ts
1976
2261
  function searchQualityToMode(quality) {
@@ -2069,6 +2354,22 @@ async function multiQuerySearch(transport, collection, vectors, options) {
2069
2354
  throwOnError(response, `Collection '${collection}'`);
2070
2355
  return response.data?.results ?? [];
2071
2356
  }
2357
+ async function multiQuerySearchIds(transport, collection, vectors, options) {
2358
+ const formattedVectors = vectors.map(toNumberArray);
2359
+ const response = await transport.requestJson("POST", `${collectionPath(collection)}/search/multi/ids`, {
2360
+ vectors: formattedVectors,
2361
+ top_k: options?.k ?? 10,
2362
+ strategy: options?.fusion ?? "rrf",
2363
+ rrf_k: options?.fusionParams?.k ?? 60,
2364
+ avg_weight: options?.fusionParams?.avgWeight,
2365
+ max_weight: options?.fusionParams?.maxWeight,
2366
+ hit_weight: options?.fusionParams?.hitWeight,
2367
+ dense_weight: options?.fusionParams?.denseWeight,
2368
+ sparse_weight: options?.fusionParams?.sparseWeight
2369
+ });
2370
+ throwOnError(response, `Collection '${collection}'`);
2371
+ return response.data?.results ?? [];
2372
+ }
2072
2373
  async function sparseSearchNamed(transport, collection, query3, indexName, options) {
2073
2374
  const body = {
2074
2375
  sparse_vectors: { [indexName]: transport.sparseToRest(query3) },
@@ -2203,6 +2504,64 @@ async function traverseParallel(transport, collection, request2) {
2203
2504
  throwOnError(response, `Collection '${collection}'`);
2204
2505
  return toTraverseResponse(response.data);
2205
2506
  }
2507
+ async function relate(transport, collection, req) {
2508
+ const response = await transport.requestJson(
2509
+ "POST",
2510
+ `${collectionPath(collection)}/relations`,
2511
+ {
2512
+ source: req.source,
2513
+ target: req.target,
2514
+ rel_type: req.relType,
2515
+ properties: req.properties ?? {}
2516
+ }
2517
+ );
2518
+ throwOnError(response, `Collection '${collection}'`);
2519
+ return { edgeId: response.data.edge_id };
2520
+ }
2521
+ async function unrelate(transport, collection, edgeId) {
2522
+ const response = await transport.requestJson(
2523
+ "DELETE",
2524
+ `${collectionPath(collection)}/relations/${encodeURIComponent(String(edgeId))}`
2525
+ );
2526
+ if (response.error !== void 0) {
2527
+ const { code, message } = response.error;
2528
+ const err = parseVelesError(code, message);
2529
+ if (err instanceof EdgeNotFoundError) {
2530
+ return false;
2531
+ }
2532
+ if (code === "NOT_FOUND") {
2533
+ return false;
2534
+ }
2535
+ throwOnError(response, `Collection '${collection}'`);
2536
+ }
2537
+ return true;
2538
+ }
2539
+ async function getRelations(transport, collection, pointId) {
2540
+ const response = await transport.requestJson(
2541
+ "GET",
2542
+ `${collectionPath(collection)}/points/${encodeURIComponent(String(pointId))}/relations`
2543
+ );
2544
+ throwOnError(response, `Collection '${collection}'`);
2545
+ const raw = response.data;
2546
+ return {
2547
+ edges: raw.edges.map((e) => ({
2548
+ id: e.id,
2549
+ source: e.source,
2550
+ target: e.target,
2551
+ relType: e.rel_type,
2552
+ properties: e.properties
2553
+ })),
2554
+ count: raw.count
2555
+ };
2556
+ }
2557
+ async function setTtlDurable(transport, collection, pointId, ttlSeconds) {
2558
+ const response = await transport.requestJson(
2559
+ "PATCH",
2560
+ `${collectionPath(collection)}/points/${encodeURIComponent(String(pointId))}/ttl`,
2561
+ { ttl_seconds: ttlSeconds }
2562
+ );
2563
+ throwOnError(response, `Collection '${collection}'`);
2564
+ }
2206
2565
 
2207
2566
  // src/backends/query-backend.ts
2208
2567
  function isLikelyAggregationQuery(queryString) {
@@ -2228,7 +2587,7 @@ async function query(transport, collection, queryString, params, options) {
2228
2587
  );
2229
2588
  throwOnError(response, `Collection '${collection}'`);
2230
2589
  const rawData = response.data;
2231
- if (rawData && Object.prototype.hasOwnProperty.call(rawData, "result")) {
2590
+ if (rawData != null && Object.prototype.hasOwnProperty.call(rawData, "result")) {
2232
2591
  return {
2233
2592
  result: rawData.result,
2234
2593
  stats: {
@@ -2335,31 +2694,6 @@ async function collectionSanity(transport, collection) {
2335
2694
  };
2336
2695
  }
2337
2696
 
2338
- // src/backends/scroll-backend.ts
2339
- async function scroll(transport, collection, request2) {
2340
- const body = {};
2341
- if (request2?.cursor !== void 0) {
2342
- body.cursor = request2.cursor;
2343
- }
2344
- if (request2?.batchSize !== void 0) {
2345
- body.batch_size = request2.batchSize;
2346
- }
2347
- if (request2?.filter !== void 0) {
2348
- body.filter = request2.filter;
2349
- }
2350
- const response = await transport.requestJson(
2351
- "POST",
2352
- `${collectionPath(collection)}/points/scroll`,
2353
- body
2354
- );
2355
- throwOnError(response, `Collection '${collection}'`);
2356
- const data = response.data;
2357
- return {
2358
- points: data.points,
2359
- nextCursor: data.next_cursor
2360
- };
2361
- }
2362
-
2363
2697
  // src/backends/admin-backend.ts
2364
2698
  function mapStatsResponse(data) {
2365
2699
  let columnStats;
@@ -2523,7 +2857,7 @@ async function streamInsert(transport, collection, docs) {
2523
2857
  throw new BackpressureError();
2524
2858
  }
2525
2859
  if (!response.ok && response.status !== 202) {
2526
- const data = await response.json().catch(() => ({}));
2860
+ const data = await safeJsonParse(response);
2527
2861
  const errorPayload = transport.extractErrorPayload(data);
2528
2862
  throw new VelesDBError(
2529
2863
  errorPayload.message ?? `HTTP ${response.status}`,
@@ -2545,9 +2879,35 @@ async function streamInsert(transport, collection, docs) {
2545
2879
  }
2546
2880
  }
2547
2881
  }
2882
+ async function enableStreaming(transport, collection, config) {
2883
+ const body = {};
2884
+ if (config?.bufferSize !== void 0) {
2885
+ body.buffer_size = config.bufferSize;
2886
+ }
2887
+ if (config?.batchSize !== void 0) {
2888
+ body.batch_size = config.batchSize;
2889
+ }
2890
+ if (config?.flushIntervalMs !== void 0) {
2891
+ body.flush_interval_ms = config.flushIntervalMs;
2892
+ }
2893
+ const response = await transport.requestJson(
2894
+ "POST",
2895
+ `${collectionPath(collection)}/stream/enable`,
2896
+ body
2897
+ );
2898
+ throwOnError(response);
2899
+ }
2900
+ function requireSafeRangeId(restId) {
2901
+ if (typeof restId === "string") {
2902
+ throw new ValidationError(
2903
+ `streamUpsertPoints requires ids in the JS safe integer range (0..${Number.MAX_SAFE_INTEGER}); use upsert/upsertBatch for string ids above it. Received: ${restId}`
2904
+ );
2905
+ }
2906
+ return restId;
2907
+ }
2548
2908
  async function streamUpsertPoints(transport, collection, docs) {
2549
2909
  const ndjsonLines = docs.map((doc) => {
2550
- const restId = transport.parseRestPointId(doc.id);
2910
+ const restId = requireSafeRangeId(transport.parseRestPointId(doc.id));
2551
2911
  const vector = toNumberArray(doc.vector);
2552
2912
  const point = {
2553
2913
  id: restId,
@@ -2581,14 +2941,14 @@ async function streamUpsertPoints(transport, collection, docs) {
2581
2941
  throw new BackpressureError();
2582
2942
  }
2583
2943
  if (!response.ok) {
2584
- const data2 = await response.json().catch(() => ({}));
2944
+ const data2 = await safeJsonParse(response);
2585
2945
  const errorPayload = transport.extractErrorPayload(data2);
2586
2946
  throw new VelesDBError(
2587
2947
  errorPayload.message ?? `HTTP ${response.status}`,
2588
2948
  errorPayload.code ?? transport.mapStatusToErrorCode(response.status)
2589
2949
  );
2590
2950
  }
2591
- const data = await response.json().catch(() => ({}));
2951
+ const data = await safeJsonParse(response);
2592
2952
  return {
2593
2953
  message: typeof data.message === "string" ? data.message : "Stream processed",
2594
2954
  inserted: typeof data.inserted === "number" ? data.inserted : 0,
@@ -2673,6 +3033,10 @@ var RestBackend = class {
2673
3033
  this.ensureInitialized();
2674
3034
  return upsertBatch(buildCrudTransport(this.httpConfig), c, d);
2675
3035
  }
3036
+ async upsertBatchRaw(c, d) {
3037
+ this.ensureInitialized();
3038
+ return upsertBatchRaw(buildRawBulkTransport(this.httpConfig), c, d, d[0]?.vector.length ?? 0);
3039
+ }
2676
3040
  async delete(c, id) {
2677
3041
  this.ensureInitialized();
2678
3042
  return deletePoint(buildCrudTransport(this.httpConfig), c, id);
@@ -2738,6 +3102,22 @@ var RestBackend = class {
2738
3102
  this.ensureInitialized();
2739
3103
  return graphSearch(buildBaseTransport(this.httpConfig), c, r);
2740
3104
  }
3105
+ async relate(c, req) {
3106
+ this.ensureInitialized();
3107
+ return relate(buildCrudTransport(this.httpConfig), c, req);
3108
+ }
3109
+ async unrelate(c, edgeId) {
3110
+ this.ensureInitialized();
3111
+ return unrelate(buildCrudTransport(this.httpConfig), c, edgeId);
3112
+ }
3113
+ async getRelations(c, pointId) {
3114
+ this.ensureInitialized();
3115
+ return getRelations(buildCrudTransport(this.httpConfig), c, pointId);
3116
+ }
3117
+ async setTtlDurable(c, pointId, ttlSeconds) {
3118
+ this.ensureInitialized();
3119
+ return setTtlDurable(buildCrudTransport(this.httpConfig), c, pointId, ttlSeconds);
3120
+ }
2741
3121
  // Search
2742
3122
  async search(c, q, o) {
2743
3123
  this.ensureInitialized();
@@ -2759,6 +3139,10 @@ var RestBackend = class {
2759
3139
  this.ensureInitialized();
2760
3140
  return multiQuerySearch(buildSearchTransport(this.httpConfig), c, v, o);
2761
3141
  }
3142
+ async multiQuerySearchIds(c, v, o) {
3143
+ this.ensureInitialized();
3144
+ return multiQuerySearchIds(buildSearchTransport(this.httpConfig), c, v, o);
3145
+ }
2762
3146
  async searchIds(c, q, o) {
2763
3147
  this.ensureInitialized();
2764
3148
  return searchIds(buildSearchTransport(this.httpConfig), c, q, o);
@@ -2845,6 +3229,10 @@ var RestBackend = class {
2845
3229
  this.ensureInitialized();
2846
3230
  return trainPq(buildStreamingTransport(this.httpConfig), c, o);
2847
3231
  }
3232
+ async enableStreaming(c, cfg) {
3233
+ this.ensureInitialized();
3234
+ return enableStreaming(buildStreamingTransport(this.httpConfig), c, cfg);
3235
+ }
2848
3236
  async streamInsert(c, d) {
2849
3237
  this.ensureInitialized();
2850
3238
  return streamInsert(buildStreamingTransport(this.httpConfig), c, d);
@@ -2870,6 +3258,14 @@ var RestBackend = class {
2870
3258
  this.ensureInitialized();
2871
3259
  return recallEpisodicEvents(buildAgentMemoryTransport(this.httpConfig, (col, emb, opts) => this.search(col, emb, opts)), c, e, k);
2872
3260
  }
3261
+ async recallRecentEvents(c, since) {
3262
+ this.ensureInitialized();
3263
+ return recallRecentEvents(buildAgentMemoryTransport(this.httpConfig, (col, emb, opts) => this.search(col, emb, opts)), c, since);
3264
+ }
3265
+ async recallOlderThanEvents(c, before) {
3266
+ this.ensureInitialized();
3267
+ return recallOlderThanEvents(buildAgentMemoryTransport(this.httpConfig, (col, emb, opts) => this.search(col, emb, opts)), c, before);
3268
+ }
2873
3269
  async storeProceduralPattern(c, p) {
2874
3270
  this.ensureInitialized();
2875
3271
  return storeProceduralPattern(buildAgentMemoryTransport(this.httpConfig, (col, emb, opts) => this.search(col, emb, opts)), c, p);
@@ -2907,15 +3303,29 @@ var AgentMemoryClient = class {
2907
3303
  async searchFacts(collection, embedding, k = 5) {
2908
3304
  return this.backend.searchSemanticMemory(collection, embedding, k);
2909
3305
  }
2910
- /** Record an episodic event. Returns the generated point ID. */
3306
+ /** Record an episodic event. Returns the point ID (string, u64-safe). */
2911
3307
  async recordEvent(collection, event) {
2912
3308
  return this.backend.recordEpisodicEvent(collection, event);
2913
3309
  }
2914
- /** Recall episodic events */
3310
+ /** Recall episodic events by vector similarity. */
2915
3311
  async recallEvents(collection, embedding, k = 5) {
2916
3312
  return this.backend.recallEpisodicEvents(collection, embedding, k);
2917
3313
  }
2918
- /** Store a procedural pattern. Returns the generated point ID. */
3314
+ /**
3315
+ * Recall episodic events most-recent-first, optionally bounded below by
3316
+ * `since` (inclusive unix-seconds). Mirrors core `episodic.recent(since)`.
3317
+ */
3318
+ async recallRecent(collection, since) {
3319
+ return this.backend.recallRecentEvents(collection, since);
3320
+ }
3321
+ /**
3322
+ * Recall episodic events strictly older than `before` (unix-seconds),
3323
+ * most-recent-first. Mirrors core `episodic.older_than(before)`.
3324
+ */
3325
+ async recallOlderThan(collection, before) {
3326
+ return this.backend.recallOlderThanEvents(collection, before);
3327
+ }
3328
+ /** Store a procedural pattern. Returns the point ID (string, u64-safe). */
2919
3329
  async learnProcedure(collection, pattern) {
2920
3330
  return this.backend.storeProceduralPattern(collection, pattern);
2921
3331
  }
@@ -2923,7 +3333,12 @@ var AgentMemoryClient = class {
2923
3333
  async recallProcedures(collection, embedding, k = 5) {
2924
3334
  return this.backend.matchProceduralPatterns(collection, embedding, k);
2925
3335
  }
2926
- /** Delete a memory entry (fact, event, or procedure) by its point ID. */
3336
+ /**
3337
+ * Delete a memory entry (fact, event, or procedure) by its point ID.
3338
+ *
3339
+ * Accepts the `string` ids returned by `recordEvent` / `learnProcedure`
3340
+ * (u64-safe decimal strings) as well as numeric ids.
3341
+ */
2927
3342
  async deleteMemory(collection, id) {
2928
3343
  return this.backend.delete(collection, id);
2929
3344
  }
@@ -2957,11 +3372,10 @@ function validateDocument(doc, config) {
2957
3372
  validateRestPointId(doc.id, config);
2958
3373
  }
2959
3374
  function validateRestPointId(id, config) {
2960
- if (config.backend === "rest" && (typeof id !== "number" || !Number.isInteger(id) || id < 0 || id > Number.MAX_SAFE_INTEGER)) {
2961
- throw new ValidationError(
2962
- `REST backend requires numeric u64-compatible document IDs in JS safe integer range (0..${Number.MAX_SAFE_INTEGER})`
2963
- );
3375
+ if (config.backend !== "rest") {
3376
+ return;
2964
3377
  }
3378
+ parseRestPointId(id);
2965
3379
  }
2966
3380
 
2967
3381
  // src/client/search-methods.ts
@@ -3001,9 +3415,21 @@ function multiQuerySearch2(backend, collection, vectors, options) {
3001
3415
  }
3002
3416
  return backend.multiQuerySearch(collection, vectors, options);
3003
3417
  }
3418
+ function multiQuerySearchIds2(backend, collection, vectors, options) {
3419
+ if (!Array.isArray(vectors) || vectors.length === 0) {
3420
+ throw new ValidationError("Vectors must be a non-empty array");
3421
+ }
3422
+ for (const v of vectors) {
3423
+ requireVector(v, "Each vector");
3424
+ }
3425
+ return backend.multiQuerySearchIds(collection, vectors, options);
3426
+ }
3004
3427
  function trainPq2(backend, collection, options) {
3005
3428
  return backend.trainPq(collection, options);
3006
3429
  }
3430
+ function enableStreaming2(backend, collection, config) {
3431
+ return backend.enableStreaming(collection, config);
3432
+ }
3007
3433
  function streamInsert2(backend, config, collection, docs) {
3008
3434
  validateDocsBatch(docs, (doc) => {
3009
3435
  validateDocument(doc, config);
@@ -3142,6 +3568,40 @@ function graphSearch2(backend, collection, request2) {
3142
3568
  requireNonEmptyString(collection, "Collection");
3143
3569
  return backend.graphSearch(collection, request2);
3144
3570
  }
3571
+ function relate2(backend, collection, req) {
3572
+ requireNonEmptyString(collection, "Collection");
3573
+ if (!req.relType || typeof req.relType !== "string") {
3574
+ throw new ValidationError("Relation type is required and must be a string");
3575
+ }
3576
+ if (!isGraphNodeId(req.source) || !isGraphNodeId(req.target)) {
3577
+ throw new ValidationError("Source and target must be numbers or strings");
3578
+ }
3579
+ return backend.relate(collection, req);
3580
+ }
3581
+ function unrelate2(backend, collection, edgeId) {
3582
+ requireNonEmptyString(collection, "Collection");
3583
+ if (!isGraphNodeId(edgeId)) {
3584
+ throw new ValidationError("Edge ID must be a number or string");
3585
+ }
3586
+ return backend.unrelate(collection, edgeId);
3587
+ }
3588
+ function getRelations2(backend, collection, pointId) {
3589
+ requireNonEmptyString(collection, "Collection");
3590
+ if (!isGraphNodeId(pointId)) {
3591
+ throw new ValidationError("Point ID must be a number or string");
3592
+ }
3593
+ return backend.getRelations(collection, pointId);
3594
+ }
3595
+ function setTtlDurable2(backend, collection, pointId, ttlSeconds) {
3596
+ requireNonEmptyString(collection, "Collection");
3597
+ if (!isGraphNodeId(pointId)) {
3598
+ throw new ValidationError("Point ID must be a number or string");
3599
+ }
3600
+ if (typeof ttlSeconds !== "number" || ttlSeconds < 0) {
3601
+ throw new ValidationError("ttlSeconds must be a non-negative number");
3602
+ }
3603
+ return backend.setTtlDurable(collection, pointId, ttlSeconds);
3604
+ }
3145
3605
 
3146
3606
  // src/client.ts
3147
3607
  var VelesDB = class {
@@ -3238,6 +3698,24 @@ var VelesDB = class {
3238
3698
  });
3239
3699
  await this.backend.upsertBatch(collection, docs);
3240
3700
  }
3701
+ /**
3702
+ * Bulk upsert via the binary wire format (REST backend only).
3703
+ *
3704
+ * Encodes `(id, vector)` pairs into the deterministic VRB1 binary layout
3705
+ * and sends them as a single `application/octet-stream` request, avoiding
3706
+ * per-point JSON overhead. Payloads are not carried — use
3707
+ * {@link upsertBatch} when you need them. Throws a not-supported error on
3708
+ * the WASM backend.
3709
+ *
3710
+ * @returns the number of points the server reports as inserted.
3711
+ */
3712
+ async upsertBatchRaw(collection, docs) {
3713
+ this.ensureInitialized();
3714
+ validateDocsBatch(docs, (doc) => {
3715
+ validateDocument(doc, this.config);
3716
+ });
3717
+ return this.backend.upsertBatchRaw(collection, docs);
3718
+ }
3241
3719
  async delete(collection, id) {
3242
3720
  this.ensureInitialized();
3243
3721
  validateRestPointId(id, this.config);
@@ -3285,6 +3763,11 @@ var VelesDB = class {
3285
3763
  this.ensureInitialized();
3286
3764
  return multiQuerySearch2(this.backend, collection, vectors, options);
3287
3765
  }
3766
+ /** Multi-query fusion search returning only IDs and scores (no payloads). */
3767
+ async multiQuerySearchIds(collection, vectors, options) {
3768
+ this.ensureInitialized();
3769
+ return multiQuerySearchIds2(this.backend, collection, vectors, options);
3770
+ }
3288
3771
  /**
3289
3772
  * Pure sparse search against a named sparse index.
3290
3773
  *
@@ -3315,6 +3798,10 @@ var VelesDB = class {
3315
3798
  this.ensureInitialized();
3316
3799
  return trainPq2(this.backend, collection, options);
3317
3800
  }
3801
+ async enableStreaming(collection, config) {
3802
+ this.ensureInitialized();
3803
+ return enableStreaming2(this.backend, collection, config);
3804
+ }
3318
3805
  async streamInsert(collection, docs) {
3319
3806
  this.ensureInitialized();
3320
3807
  return streamInsert2(this.backend, this.config, collection, docs);
@@ -3439,6 +3926,22 @@ var VelesDB = class {
3439
3926
  this.ensureInitialized();
3440
3927
  return graphSearch2(this.backend, collection, request2);
3441
3928
  }
3929
+ async relate(collection, req) {
3930
+ this.ensureInitialized();
3931
+ return relate2(this.backend, collection, req);
3932
+ }
3933
+ async unrelate(collection, edgeId) {
3934
+ this.ensureInitialized();
3935
+ return unrelate2(this.backend, collection, edgeId);
3936
+ }
3937
+ async getRelations(collection, pointId) {
3938
+ this.ensureInitialized();
3939
+ return getRelations2(this.backend, collection, pointId);
3940
+ }
3941
+ async setTtlDurable(collection, pointId, ttlSeconds) {
3942
+ this.ensureInitialized();
3943
+ return setTtlDurable2(this.backend, collection, pointId, ttlSeconds);
3944
+ }
3442
3945
  // ========================================================================
3443
3946
  // Capabilities & Backend Info
3444
3947
  // ========================================================================