@wiscale/velesdb-sdk 1.17.0 → 2.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
@@ -199,7 +199,7 @@ var WASM_CAPABILITIES = Object.freeze({
199
199
  agentMemory: false,
200
200
  streamInsert: false,
201
201
  pqTraining: false,
202
- velesqlQuery: true,
202
+ velesqlQuery: false,
203
203
  collectionIntrospection: false
204
204
  });
205
205
 
@@ -437,20 +437,47 @@ async function wasmMultiQuerySearch(ctx, collectionName, vectors, options) {
437
437
  );
438
438
  return raw.map((r) => mapWasmResult(ctx, collection, r));
439
439
  }
440
- async function wasmQuery(ctx, collectionName, _queryString, params, _options) {
440
+ 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;
441
+ function parsePureNearQuery(queryString) {
442
+ const match = PURE_NEAR_QUERY.exec(queryString);
443
+ if (!match) {
444
+ throw new VelesDBError(
445
+ `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}`,
446
+ "NOT_SUPPORTED"
447
+ );
448
+ }
449
+ const parsed = { from: match[1], param: match[2] };
450
+ if (match[3] !== void 0) {
451
+ parsed.limit = Number(match[3]);
452
+ }
453
+ return parsed;
454
+ }
455
+ function resolveQueryK(limit, requestedK) {
456
+ if (limit !== void 0) {
457
+ return limit;
458
+ }
459
+ return typeof requestedK === "number" && Number.isInteger(requestedK) && requestedK > 0 ? requestedK : 10;
460
+ }
461
+ async function wasmQuery(ctx, collectionName, queryString, params, _options) {
441
462
  const collection = ctx.getCollection(collectionName);
442
463
  if (!collection) {
443
464
  throw new NotFoundError(`Collection '${collectionName}'`);
444
465
  }
445
- const paramsVector = params?.q;
466
+ const parsed = parsePureNearQuery(queryString);
467
+ if (parsed.from !== collectionName) {
468
+ throw new VelesDBError(
469
+ `Query targets collection '${parsed.from}' but was executed against '${collectionName}'.`,
470
+ "BAD_REQUEST"
471
+ );
472
+ }
473
+ const paramsVector = params?.[parsed.param];
446
474
  if (!Array.isArray(paramsVector) && !(paramsVector instanceof Float32Array)) {
447
475
  throw new VelesDBError(
448
- "WASM query() expects params.q to contain the query embedding vector.",
476
+ `WASM query() expects params.${parsed.param} to contain the query embedding vector.`,
449
477
  "BAD_REQUEST"
450
478
  );
451
479
  }
452
- const requestedK = params?.k;
453
- const k = typeof requestedK === "number" && Number.isInteger(requestedK) && requestedK > 0 ? requestedK : 10;
480
+ const k = resolveQueryK(parsed.limit, params?.k);
454
481
  const raw = collection.store.query(
455
482
  paramsVector instanceof Float32Array ? paramsVector : new Float32Array(paramsVector),
456
483
  k
@@ -950,6 +977,12 @@ async function wasmRecordEpisodicEvent(_collection, _event) {
950
977
  async function wasmRecallEpisodicEvents(_collection, _embedding, _k) {
951
978
  wasmNotSupported("Agent memory");
952
979
  }
980
+ async function wasmRecallRecentEvents(_collection, _since) {
981
+ wasmNotSupported("Agent memory");
982
+ }
983
+ async function wasmRecallOlderThanEvents(_collection, _before) {
984
+ wasmNotSupported("Agent memory");
985
+ }
953
986
  async function wasmStoreProceduralPattern(_collection, _pattern) {
954
987
  wasmNotSupported("Agent memory");
955
988
  }
@@ -997,6 +1030,18 @@ function wasmUpsertNodePayload(_c, _id, _p) {
997
1030
  function wasmGraphSearch(_c, _r) {
998
1031
  return Promise.resolve(wasmNotSupported("Graph search"));
999
1032
  }
1033
+ function wasmRelate(_c, _req) {
1034
+ return Promise.resolve(wasmNotSupported("Relation edges"));
1035
+ }
1036
+ function wasmUnrelate(_c, _id) {
1037
+ return Promise.resolve(wasmNotSupported("Relation edge removal"));
1038
+ }
1039
+ function wasmGetRelations(_c, _id) {
1040
+ return Promise.resolve(wasmNotSupported("Relation edges"));
1041
+ }
1042
+ function wasmSetTtlDurable(_c, _id, _ttl) {
1043
+ return Promise.resolve(wasmNotSupported("Durable TTL"));
1044
+ }
1000
1045
 
1001
1046
  // src/backends/wasm.ts
1002
1047
  var WasmBackend = class {
@@ -1351,6 +1396,14 @@ var WasmBackend = class {
1351
1396
  this.ensureInitialized();
1352
1397
  return wasmRecallEpisodicEvents(c, e, k);
1353
1398
  }
1399
+ async recallRecentEvents(c, since) {
1400
+ this.ensureInitialized();
1401
+ return wasmRecallRecentEvents(c, since);
1402
+ }
1403
+ async recallOlderThanEvents(c, before) {
1404
+ this.ensureInitialized();
1405
+ return wasmRecallOlderThanEvents(c, before);
1406
+ }
1354
1407
  async storeProceduralPattern(c, p) {
1355
1408
  this.ensureInitialized();
1356
1409
  return wasmStoreProceduralPattern(c, p);
@@ -1412,16 +1465,43 @@ var WasmBackend = class {
1412
1465
  this.ensureInitialized();
1413
1466
  return wasmSparseSearchNamed(c, q, idx, o);
1414
1467
  }
1468
+ async relate(c, req) {
1469
+ this.ensureInitialized();
1470
+ return wasmRelate(c, req);
1471
+ }
1472
+ async unrelate(c, edgeId) {
1473
+ this.ensureInitialized();
1474
+ return wasmUnrelate(c, edgeId);
1475
+ }
1476
+ async getRelations(c, pointId) {
1477
+ this.ensureInitialized();
1478
+ return wasmGetRelations(c, pointId);
1479
+ }
1480
+ async setTtlDurable(c, pointId, ttlSeconds) {
1481
+ this.ensureInitialized();
1482
+ return wasmSetTtlDurable(c, pointId, ttlSeconds);
1483
+ }
1415
1484
  };
1416
1485
 
1417
1486
  // src/backends/crud-backend.ts
1487
+ var U64_MAX = 18446744073709551615n;
1488
+ function coerceDecimalStringId(id) {
1489
+ if (!/^\d+$/.test(id)) return NaN;
1490
+ const big = BigInt(id);
1491
+ if (big > U64_MAX) return NaN;
1492
+ return big > BigInt(Number.MAX_SAFE_INTEGER) ? id : Number(id);
1493
+ }
1418
1494
  function parseRestPointId(id) {
1419
- if (typeof id !== "number" || !Number.isFinite(id) || id < 0 || !Number.isInteger(id) || id > Number.MAX_SAFE_INTEGER) {
1495
+ const coerced = typeof id === "string" ? coerceDecimalStringId(id) : id;
1496
+ if (typeof coerced === "string") {
1497
+ return coerced;
1498
+ }
1499
+ if (!Number.isFinite(coerced) || coerced < 0 || !Number.isInteger(coerced) || coerced > Number.MAX_SAFE_INTEGER) {
1420
1500
  throw new ValidationError(
1421
- `REST backend requires numeric u64-compatible IDs in JS safe integer range (0..${Number.MAX_SAFE_INTEGER}). Received: ${String(id)}`
1501
+ `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
1502
  );
1423
1503
  }
1424
- return id;
1504
+ return coerced;
1425
1505
  }
1426
1506
  function sparseVectorToRestFormat(sv) {
1427
1507
  const result = {};
@@ -1815,16 +1895,13 @@ async function getEdgeCount(transport, collection) {
1815
1895
  return response.data?.count ?? 0;
1816
1896
  }
1817
1897
  async function listNodes(transport, collection) {
1818
- const response = await transport.requestJson(
1819
- "GET",
1820
- `${collectionPath(collection)}/graph/nodes`
1821
- );
1898
+ const response = await transport.requestJson("GET", `${collectionPath(collection)}/graph/nodes`);
1822
1899
  throwOnError(response, `Collection '${collection}'`);
1823
1900
  const data = response.data;
1824
1901
  return { nodeIds: data.node_ids, count: data.count };
1825
1902
  }
1826
- function idToNumber(id) {
1827
- return typeof id === "string" ? Number(id) : id;
1903
+ function idToGraphId(id) {
1904
+ return id;
1828
1905
  }
1829
1906
  async function getNodeEdges(transport, collection, nodeId, options) {
1830
1907
  const params = new URLSearchParams();
@@ -1835,9 +1912,9 @@ async function getNodeEdges(transport, collection, nodeId, options) {
1835
1912
  const response = await transport.requestJson("GET", url);
1836
1913
  throwOnError(response, `Collection '${collection}'`);
1837
1914
  return (response.data?.edges ?? []).map((e) => ({
1838
- id: idToNumber(e.id),
1839
- source: idToNumber(e.source),
1840
- target: idToNumber(e.target),
1915
+ id: e.id,
1916
+ source: e.source,
1917
+ target: e.target,
1841
1918
  label: e.label,
1842
1919
  properties: e.properties
1843
1920
  }));
@@ -1847,7 +1924,7 @@ async function getNodePayload(transport, collection, nodeId) {
1847
1924
  throwOnError(response, `Collection '${collection}'`);
1848
1925
  const data = response.data;
1849
1926
  return {
1850
- nodeId: idToNumber(data.node_id),
1927
+ nodeId: idToGraphId(data.node_id),
1851
1928
  payload: data.payload
1852
1929
  };
1853
1930
  }
@@ -1871,7 +1948,7 @@ async function graphSearch(transport, collection, request2) {
1871
1948
  throwOnError(response, `Collection '${collection}'`);
1872
1949
  const items = (response.data?.results ?? []).map(
1873
1950
  (r) => ({
1874
- id: idToNumber(r.id),
1951
+ id: idToGraphId(r.id),
1875
1952
  score: r.score,
1876
1953
  payload: r.payload
1877
1954
  })
@@ -1879,6 +1956,31 @@ async function graphSearch(transport, collection, request2) {
1879
1956
  return { results: items };
1880
1957
  }
1881
1958
 
1959
+ // src/backends/scroll-backend.ts
1960
+ async function scroll(transport, collection, request2) {
1961
+ const body = {};
1962
+ if (request2?.cursor !== void 0) {
1963
+ body.cursor = request2.cursor;
1964
+ }
1965
+ if (request2?.batchSize !== void 0) {
1966
+ body.batch_size = request2.batchSize;
1967
+ }
1968
+ if (request2?.filter !== void 0) {
1969
+ body.filter = request2.filter;
1970
+ }
1971
+ const response = await transport.requestJson(
1972
+ "POST",
1973
+ `${collectionPath(collection)}/points/scroll`,
1974
+ body
1975
+ );
1976
+ throwOnError(response, `Collection '${collection}'`);
1977
+ const data = response.data;
1978
+ return {
1979
+ points: data.points,
1980
+ nextCursor: data.next_cursor
1981
+ };
1982
+ }
1983
+
1882
1984
  // src/backends/agent-memory-backend.ts
1883
1985
  var _idCounter = 0;
1884
1986
  var _lastTimestamp = 0;
@@ -1896,18 +1998,28 @@ function generateUniqueId() {
1896
1998
  }
1897
1999
  return _lastTimestamp * 1e3 + _idCounter;
1898
2000
  }
2001
+ function memoryIdToString(id) {
2002
+ return String(id);
2003
+ }
2004
+ function nowUnixSeconds() {
2005
+ return Math.floor(Date.now() / 1e3);
2006
+ }
1899
2007
  async function storeSemanticFact(transport, collection, entry) {
1900
2008
  const response = await transport.requestJson(
1901
2009
  "POST",
1902
2010
  `${collectionPath(collection)}/points`,
1903
2011
  {
1904
2012
  points: [{
1905
- id: entry.id,
2013
+ id: parseRestPointId(entry.id),
1906
2014
  vector: entry.embedding,
1907
2015
  payload: {
2016
+ // Caller metadata is spread first so the reserved keys below
2017
+ // (`_memory_type`, `content`) always win and cannot be clobbered.
2018
+ ...entry.metadata,
1908
2019
  _memory_type: "semantic",
1909
- text: entry.text,
1910
- ...entry.metadata
2020
+ // `content` matches the core semantic store and the server/Python
2021
+ // payload field (BREAKING: was `text` before this change).
2022
+ content: entry.text
1911
2023
  }
1912
2024
  }]
1913
2025
  }
@@ -1918,7 +2030,8 @@ async function searchSemanticMemory(transport, collection, embedding, k = 5) {
1918
2030
  return transport.searchVectors(collection, embedding, k, { _memory_type: "semantic" });
1919
2031
  }
1920
2032
  async function recordEpisodicEvent(transport, collection, event) {
1921
- const id = generateUniqueId();
2033
+ const id = event.id !== void 0 ? parseRestPointId(event.id) : generateUniqueId();
2034
+ const timestamp = event.timestamp ?? nowUnixSeconds();
1922
2035
  const response = await transport.requestJson(
1923
2036
  "POST",
1924
2037
  `${collectionPath(collection)}/points`,
@@ -1927,42 +2040,83 @@ async function recordEpisodicEvent(transport, collection, event) {
1927
2040
  id,
1928
2041
  vector: event.embedding,
1929
2042
  payload: {
2043
+ // Caller-supplied data/metadata is spread first so the reserved
2044
+ // keys below (`_memory_type`, `event_type`, `timestamp`) always
2045
+ // win and cannot be clobbered.
2046
+ ...event.data,
2047
+ ...event.metadata,
1930
2048
  _memory_type: "episodic",
1931
2049
  event_type: event.eventType,
1932
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1933
- ...event.data,
1934
- ...event.metadata
2050
+ // NUMERIC unix-seconds, mirroring the core episodic store so
2051
+ // recallRecent/recallOlderThan can range-filter on it.
2052
+ timestamp
1935
2053
  }
1936
2054
  }]
1937
2055
  }
1938
2056
  );
1939
2057
  throwOnError(response);
2058
+ return memoryIdToString(id);
1940
2059
  }
1941
2060
  async function recallEpisodicEvents(transport, collection, embedding, k = 5) {
1942
2061
  return transport.searchVectors(collection, embedding, k, { _memory_type: "episodic" });
1943
2062
  }
1944
2063
  async function storeProceduralPattern(transport, collection, pattern) {
1945
- const id = generateUniqueId();
2064
+ const id = pattern.id !== void 0 ? parseRestPointId(pattern.id) : generateUniqueId();
1946
2065
  const response = await transport.requestJson(
1947
2066
  "POST",
1948
2067
  `${collectionPath(collection)}/points`,
1949
2068
  {
1950
2069
  points: [{
1951
2070
  id,
2071
+ vector: pattern.embedding,
1952
2072
  payload: {
2073
+ // Caller metadata is spread first so the reserved keys below
2074
+ // (`_memory_type`, `name`, `steps`) always win and cannot be
2075
+ // clobbered.
2076
+ ...pattern.metadata,
1953
2077
  _memory_type: "procedural",
1954
2078
  name: pattern.name,
1955
- steps: pattern.steps,
1956
- ...pattern.metadata
2079
+ steps: pattern.steps
1957
2080
  }
1958
2081
  }]
1959
2082
  }
1960
2083
  );
1961
2084
  throwOnError(response);
2085
+ return memoryIdToString(id);
1962
2086
  }
1963
2087
  async function matchProceduralPatterns(transport, collection, embedding, k = 5) {
1964
2088
  return transport.searchVectors(collection, embedding, k, { _memory_type: "procedural" });
1965
2089
  }
2090
+ function toEpisodicRecord(point) {
2091
+ const payload = point.payload ?? {};
2092
+ if (payload._memory_type !== "episodic") return void 0;
2093
+ if (typeof payload.timestamp !== "number") return void 0;
2094
+ return { id: String(point.id), timestamp: payload.timestamp, payload };
2095
+ }
2096
+ async function scrollEpisodicRecords(transport, collection) {
2097
+ const records = [];
2098
+ let cursor = null;
2099
+ do {
2100
+ const page = await scroll(transport, collection, {
2101
+ cursor: cursor ?? void 0,
2102
+ filter: { _memory_type: "episodic" }
2103
+ });
2104
+ for (const point of page.points) {
2105
+ const record = toEpisodicRecord(point);
2106
+ if (record !== void 0) records.push(record);
2107
+ }
2108
+ cursor = page.nextCursor;
2109
+ } while (cursor !== null && cursor !== void 0);
2110
+ return records;
2111
+ }
2112
+ async function recallRecentEvents(transport, collection, since) {
2113
+ const records = await scrollEpisodicRecords(transport, collection);
2114
+ return records.filter((r) => since === void 0 || r.timestamp >= since).sort((a, b) => b.timestamp - a.timestamp);
2115
+ }
2116
+ async function recallOlderThanEvents(transport, collection, before) {
2117
+ const records = await scrollEpisodicRecords(transport, collection);
2118
+ return records.filter((r) => r.timestamp < before).sort((a, b) => b.timestamp - a.timestamp);
2119
+ }
1966
2120
 
1967
2121
  // src/search-quality.ts
1968
2122
  function searchQualityToMode(quality) {
@@ -2112,15 +2266,29 @@ async function addEdge(transport, collection, edge) {
2112
2266
  throwOnError(response, `Collection '${collection}'`);
2113
2267
  }
2114
2268
  function toGraphEdge(e) {
2115
- const toNum = (v) => typeof v === "string" ? Number(v) : v;
2116
2269
  return {
2117
- id: toNum(e.id),
2118
- source: toNum(e.source),
2119
- target: toNum(e.target),
2270
+ id: e.id,
2271
+ source: e.source,
2272
+ target: e.target,
2120
2273
  label: e.label,
2121
2274
  properties: e.properties
2122
2275
  };
2123
2276
  }
2277
+ function toTraverseResponse(data) {
2278
+ return {
2279
+ results: data.results.map((r) => ({
2280
+ targetId: r.target_id,
2281
+ depth: r.depth,
2282
+ path: r.path
2283
+ })),
2284
+ nextCursor: data.next_cursor ?? void 0,
2285
+ hasMore: data.has_more,
2286
+ stats: {
2287
+ visited: data.stats.visited,
2288
+ depthReached: data.stats.depth_reached
2289
+ }
2290
+ };
2291
+ }
2124
2292
  async function getEdges(transport, collection, options) {
2125
2293
  const queryParams = options?.label ? `?label=${encodeURIComponent(options.label)}` : "";
2126
2294
  const response = await transport.requestJson(
@@ -2144,20 +2312,7 @@ async function traverseGraph(transport, collection, request2) {
2144
2312
  }
2145
2313
  );
2146
2314
  throwOnError(response, `Collection '${collection}'`);
2147
- const data = response.data;
2148
- return {
2149
- results: data.results.map((r) => ({
2150
- targetId: r.target_id,
2151
- depth: r.depth,
2152
- path: r.path
2153
- })),
2154
- nextCursor: data.next_cursor ?? void 0,
2155
- hasMore: data.has_more,
2156
- stats: {
2157
- visited: data.stats.visited,
2158
- depthReached: data.stats.depth_reached
2159
- }
2160
- };
2315
+ return toTraverseResponse(response.data);
2161
2316
  }
2162
2317
  async function getNodeDegree(transport, collection, nodeId) {
2163
2318
  const response = await transport.requestJson(
@@ -2192,21 +2347,66 @@ async function traverseParallel(transport, collection, request2) {
2192
2347
  }
2193
2348
  );
2194
2349
  throwOnError(response, `Collection '${collection}'`);
2195
- const data = response.data;
2350
+ return toTraverseResponse(response.data);
2351
+ }
2352
+ async function relate(transport, collection, req) {
2353
+ const response = await transport.requestJson(
2354
+ "POST",
2355
+ `${collectionPath(collection)}/relations`,
2356
+ {
2357
+ source: req.source,
2358
+ target: req.target,
2359
+ rel_type: req.relType,
2360
+ properties: req.properties ?? {}
2361
+ }
2362
+ );
2363
+ throwOnError(response, `Collection '${collection}'`);
2364
+ return { edgeId: response.data.edge_id };
2365
+ }
2366
+ async function unrelate(transport, collection, edgeId) {
2367
+ const response = await transport.requestJson(
2368
+ "DELETE",
2369
+ `${collectionPath(collection)}/relations/${encodeURIComponent(String(edgeId))}`
2370
+ );
2371
+ if (response.error !== void 0) {
2372
+ const { code, message } = response.error;
2373
+ const err = parseVelesError(code, message);
2374
+ if (err instanceof EdgeNotFoundError) {
2375
+ return false;
2376
+ }
2377
+ if (code === "NOT_FOUND") {
2378
+ return false;
2379
+ }
2380
+ throwOnError(response, `Collection '${collection}'`);
2381
+ }
2382
+ return true;
2383
+ }
2384
+ async function getRelations(transport, collection, pointId) {
2385
+ const response = await transport.requestJson(
2386
+ "GET",
2387
+ `${collectionPath(collection)}/points/${encodeURIComponent(String(pointId))}/relations`
2388
+ );
2389
+ throwOnError(response, `Collection '${collection}'`);
2390
+ const raw = response.data;
2196
2391
  return {
2197
- results: data.results.map((r) => ({
2198
- targetId: r.target_id,
2199
- depth: r.depth,
2200
- path: r.path
2392
+ edges: raw.edges.map((e) => ({
2393
+ id: e.id,
2394
+ source: e.source,
2395
+ target: e.target,
2396
+ relType: e.rel_type,
2397
+ properties: e.properties
2201
2398
  })),
2202
- nextCursor: data.next_cursor ?? void 0,
2203
- hasMore: data.has_more,
2204
- stats: {
2205
- visited: data.stats.visited,
2206
- depthReached: data.stats.depth_reached
2207
- }
2399
+ count: raw.count
2208
2400
  };
2209
2401
  }
2402
+ async function setTtlDurable(transport, collection, pointId, ttlSeconds) {
2403
+ const response = await transport.requestJson(
2404
+ "PATCH",
2405
+ `${collectionPath(collection)}/points/${encodeURIComponent(String(pointId))}/ttl`,
2406
+ { ttl_seconds: ttlSeconds }
2407
+ );
2408
+ throwOnError(response, `Collection '${collection}'`);
2409
+ }
2210
2410
 
2211
2411
  // src/backends/query-backend.ts
2212
2412
  function isLikelyAggregationQuery(queryString) {
@@ -2339,31 +2539,6 @@ async function collectionSanity(transport, collection) {
2339
2539
  };
2340
2540
  }
2341
2541
 
2342
- // src/backends/scroll-backend.ts
2343
- async function scroll(transport, collection, request2) {
2344
- const body = {};
2345
- if (request2?.cursor !== void 0) {
2346
- body.cursor = request2.cursor;
2347
- }
2348
- if (request2?.batchSize !== void 0) {
2349
- body.batch_size = request2.batchSize;
2350
- }
2351
- if (request2?.filter !== void 0) {
2352
- body.filter = request2.filter;
2353
- }
2354
- const response = await transport.requestJson(
2355
- "POST",
2356
- `${collectionPath(collection)}/points/scroll`,
2357
- body
2358
- );
2359
- throwOnError(response, `Collection '${collection}'`);
2360
- const data = response.data;
2361
- return {
2362
- points: data.points,
2363
- nextCursor: data.next_cursor
2364
- };
2365
- }
2366
-
2367
2542
  // src/backends/admin-backend.ts
2368
2543
  function mapStatsResponse(data) {
2369
2544
  let columnStats;
@@ -2549,9 +2724,17 @@ async function streamInsert(transport, collection, docs) {
2549
2724
  }
2550
2725
  }
2551
2726
  }
2727
+ function requireSafeRangeId(restId) {
2728
+ if (typeof restId === "string") {
2729
+ throw new ValidationError(
2730
+ `streamUpsertPoints requires ids in the JS safe integer range (0..${Number.MAX_SAFE_INTEGER}); use upsert/upsertBatch for string ids above it. Received: ${restId}`
2731
+ );
2732
+ }
2733
+ return restId;
2734
+ }
2552
2735
  async function streamUpsertPoints(transport, collection, docs) {
2553
2736
  const ndjsonLines = docs.map((doc) => {
2554
- const restId = transport.parseRestPointId(doc.id);
2737
+ const restId = requireSafeRangeId(transport.parseRestPointId(doc.id));
2555
2738
  const vector = toNumberArray(doc.vector);
2556
2739
  const point = {
2557
2740
  id: restId,
@@ -2742,6 +2925,22 @@ var RestBackend = class {
2742
2925
  this.ensureInitialized();
2743
2926
  return graphSearch(buildBaseTransport(this.httpConfig), c, r);
2744
2927
  }
2928
+ async relate(c, req) {
2929
+ this.ensureInitialized();
2930
+ return relate(buildCrudTransport(this.httpConfig), c, req);
2931
+ }
2932
+ async unrelate(c, edgeId) {
2933
+ this.ensureInitialized();
2934
+ return unrelate(buildCrudTransport(this.httpConfig), c, edgeId);
2935
+ }
2936
+ async getRelations(c, pointId) {
2937
+ this.ensureInitialized();
2938
+ return getRelations(buildCrudTransport(this.httpConfig), c, pointId);
2939
+ }
2940
+ async setTtlDurable(c, pointId, ttlSeconds) {
2941
+ this.ensureInitialized();
2942
+ return setTtlDurable(buildCrudTransport(this.httpConfig), c, pointId, ttlSeconds);
2943
+ }
2745
2944
  // Search
2746
2945
  async search(c, q, o) {
2747
2946
  this.ensureInitialized();
@@ -2874,6 +3073,14 @@ var RestBackend = class {
2874
3073
  this.ensureInitialized();
2875
3074
  return recallEpisodicEvents(buildAgentMemoryTransport(this.httpConfig, (col, emb, opts) => this.search(col, emb, opts)), c, e, k);
2876
3075
  }
3076
+ async recallRecentEvents(c, since) {
3077
+ this.ensureInitialized();
3078
+ return recallRecentEvents(buildAgentMemoryTransport(this.httpConfig, (col, emb, opts) => this.search(col, emb, opts)), c, since);
3079
+ }
3080
+ async recallOlderThanEvents(c, before) {
3081
+ this.ensureInitialized();
3082
+ return recallOlderThanEvents(buildAgentMemoryTransport(this.httpConfig, (col, emb, opts) => this.search(col, emb, opts)), c, before);
3083
+ }
2877
3084
  async storeProceduralPattern(c, p) {
2878
3085
  this.ensureInitialized();
2879
3086
  return storeProceduralPattern(buildAgentMemoryTransport(this.httpConfig, (col, emb, opts) => this.search(col, emb, opts)), c, p);
@@ -2890,7 +3097,16 @@ var AgentMemoryClient = class {
2890
3097
  this.backend = backend;
2891
3098
  this.config = config;
2892
3099
  }
2893
- /** Configured embedding dimension (default: 384) */
3100
+ /**
3101
+ * Advisory embedding dimension passed at construction (default: 384).
3102
+ *
3103
+ * This value is **not** enforced and does not create or size any
3104
+ * collection — the dimension that actually governs storage and search
3105
+ * is the one fixed when the collection was created
3106
+ * (`db.createCollection(name, { dimension, metric: 'cosine' })`).
3107
+ * Embeddings you pass to `storeFact` / `recordEvent` / `learnProcedure`
3108
+ * must match that collection dimension.
3109
+ */
2894
3110
  get dimension() {
2895
3111
  return this.config?.dimension ?? 384;
2896
3112
  }
@@ -2902,15 +3118,29 @@ var AgentMemoryClient = class {
2902
3118
  async searchFacts(collection, embedding, k = 5) {
2903
3119
  return this.backend.searchSemanticMemory(collection, embedding, k);
2904
3120
  }
2905
- /** Record an episodic event */
3121
+ /** Record an episodic event. Returns the point ID (string, u64-safe). */
2906
3122
  async recordEvent(collection, event) {
2907
3123
  return this.backend.recordEpisodicEvent(collection, event);
2908
3124
  }
2909
- /** Recall episodic events */
3125
+ /** Recall episodic events by vector similarity. */
2910
3126
  async recallEvents(collection, embedding, k = 5) {
2911
3127
  return this.backend.recallEpisodicEvents(collection, embedding, k);
2912
3128
  }
2913
- /** Store a procedural pattern */
3129
+ /**
3130
+ * Recall episodic events most-recent-first, optionally bounded below by
3131
+ * `since` (inclusive unix-seconds). Mirrors core `episodic.recent(since)`.
3132
+ */
3133
+ async recallRecent(collection, since) {
3134
+ return this.backend.recallRecentEvents(collection, since);
3135
+ }
3136
+ /**
3137
+ * Recall episodic events strictly older than `before` (unix-seconds),
3138
+ * most-recent-first. Mirrors core `episodic.older_than(before)`.
3139
+ */
3140
+ async recallOlderThan(collection, before) {
3141
+ return this.backend.recallOlderThanEvents(collection, before);
3142
+ }
3143
+ /** Store a procedural pattern. Returns the point ID (string, u64-safe). */
2914
3144
  async learnProcedure(collection, pattern) {
2915
3145
  return this.backend.storeProceduralPattern(collection, pattern);
2916
3146
  }
@@ -2918,6 +3148,15 @@ var AgentMemoryClient = class {
2918
3148
  async recallProcedures(collection, embedding, k = 5) {
2919
3149
  return this.backend.matchProceduralPatterns(collection, embedding, k);
2920
3150
  }
3151
+ /**
3152
+ * Delete a memory entry (fact, event, or procedure) by its point ID.
3153
+ *
3154
+ * Accepts the `string` ids returned by `recordEvent` / `learnProcedure`
3155
+ * (u64-safe decimal strings) as well as numeric ids.
3156
+ */
3157
+ async deleteMemory(collection, id) {
3158
+ return this.backend.delete(collection, id);
3159
+ }
2921
3160
  };
2922
3161
 
2923
3162
  // src/client/validation.ts
@@ -2948,11 +3187,10 @@ function validateDocument(doc, config) {
2948
3187
  validateRestPointId(doc.id, config);
2949
3188
  }
2950
3189
  function validateRestPointId(id, config) {
2951
- if (config.backend === "rest" && (typeof id !== "number" || !Number.isInteger(id) || id < 0 || id > Number.MAX_SAFE_INTEGER)) {
2952
- throw new ValidationError(
2953
- `REST backend requires numeric u64-compatible document IDs in JS safe integer range (0..${Number.MAX_SAFE_INTEGER})`
2954
- );
3190
+ if (config.backend !== "rest") {
3191
+ return;
2955
3192
  }
3193
+ parseRestPointId(id);
2956
3194
  }
2957
3195
 
2958
3196
  // src/client/search-methods.ts
@@ -3057,12 +3295,15 @@ function aggregate2(backend, queryString, params, options) {
3057
3295
  }
3058
3296
 
3059
3297
  // src/client/graph-methods.ts
3298
+ function isGraphNodeId(value) {
3299
+ return typeof value === "number" || typeof value === "string";
3300
+ }
3060
3301
  function addEdge2(backend, collection, edge) {
3061
3302
  if (!edge.label || typeof edge.label !== "string") {
3062
3303
  throw new ValidationError("Edge label is required and must be a string");
3063
3304
  }
3064
- if (typeof edge.source !== "number" || typeof edge.target !== "number") {
3065
- throw new ValidationError("Edge source and target must be numbers");
3305
+ if (!isGraphNodeId(edge.source) || !isGraphNodeId(edge.target)) {
3306
+ throw new ValidationError("Edge source and target must be numbers or strings");
3066
3307
  }
3067
3308
  return backend.addEdge(collection, edge);
3068
3309
  }
@@ -3070,8 +3311,8 @@ function getEdges2(backend, collection, options) {
3070
3311
  return backend.getEdges(collection, options);
3071
3312
  }
3072
3313
  function traverseGraph2(backend, collection, request2) {
3073
- if (typeof request2.source !== "number") {
3074
- throw new ValidationError("Source node ID must be a number");
3314
+ if (!isGraphNodeId(request2.source)) {
3315
+ throw new ValidationError("Source node ID must be a number or string");
3075
3316
  }
3076
3317
  if (request2.strategy && !["bfs", "dfs"].includes(request2.strategy)) {
3077
3318
  throw new ValidationError("Strategy must be 'bfs' or 'dfs'");
@@ -3082,6 +3323,9 @@ function traverseParallel2(backend, collection, request2) {
3082
3323
  if (!Array.isArray(request2.sources) || request2.sources.length === 0) {
3083
3324
  throw new ValidationError("At least one source node ID is required");
3084
3325
  }
3326
+ if (!request2.sources.every(isGraphNodeId)) {
3327
+ throw new ValidationError("Source node IDs must be numbers or strings");
3328
+ }
3085
3329
  return backend.traverseParallel(collection, request2);
3086
3330
  }
3087
3331
  function getNodeDegree2(backend, collection, nodeId) {
@@ -3127,6 +3371,40 @@ function graphSearch2(backend, collection, request2) {
3127
3371
  requireNonEmptyString(collection, "Collection");
3128
3372
  return backend.graphSearch(collection, request2);
3129
3373
  }
3374
+ function relate2(backend, collection, req) {
3375
+ requireNonEmptyString(collection, "Collection");
3376
+ if (!req.relType || typeof req.relType !== "string") {
3377
+ throw new ValidationError("Relation type is required and must be a string");
3378
+ }
3379
+ if (!isGraphNodeId(req.source) || !isGraphNodeId(req.target)) {
3380
+ throw new ValidationError("Source and target must be numbers or strings");
3381
+ }
3382
+ return backend.relate(collection, req);
3383
+ }
3384
+ function unrelate2(backend, collection, edgeId) {
3385
+ requireNonEmptyString(collection, "Collection");
3386
+ if (!isGraphNodeId(edgeId)) {
3387
+ throw new ValidationError("Edge ID must be a number or string");
3388
+ }
3389
+ return backend.unrelate(collection, edgeId);
3390
+ }
3391
+ function getRelations2(backend, collection, pointId) {
3392
+ requireNonEmptyString(collection, "Collection");
3393
+ if (!isGraphNodeId(pointId)) {
3394
+ throw new ValidationError("Point ID must be a number or string");
3395
+ }
3396
+ return backend.getRelations(collection, pointId);
3397
+ }
3398
+ function setTtlDurable2(backend, collection, pointId, ttlSeconds) {
3399
+ requireNonEmptyString(collection, "Collection");
3400
+ if (!isGraphNodeId(pointId)) {
3401
+ throw new ValidationError("Point ID must be a number or string");
3402
+ }
3403
+ if (typeof ttlSeconds !== "number" || ttlSeconds < 0) {
3404
+ throw new ValidationError("ttlSeconds must be a non-negative number");
3405
+ }
3406
+ return backend.setTtlDurable(collection, pointId, ttlSeconds);
3407
+ }
3130
3408
 
3131
3409
  // src/client.ts
3132
3410
  var VelesDB = class {
@@ -3424,6 +3702,22 @@ var VelesDB = class {
3424
3702
  this.ensureInitialized();
3425
3703
  return graphSearch2(this.backend, collection, request2);
3426
3704
  }
3705
+ async relate(collection, req) {
3706
+ this.ensureInitialized();
3707
+ return relate2(this.backend, collection, req);
3708
+ }
3709
+ async unrelate(collection, edgeId) {
3710
+ this.ensureInitialized();
3711
+ return unrelate2(this.backend, collection, edgeId);
3712
+ }
3713
+ async getRelations(collection, pointId) {
3714
+ this.ensureInitialized();
3715
+ return getRelations2(this.backend, collection, pointId);
3716
+ }
3717
+ async setTtlDurable(collection, pointId, ttlSeconds) {
3718
+ this.ensureInitialized();
3719
+ return setTtlDurable2(this.backend, collection, pointId, ttlSeconds);
3720
+ }
3427
3721
  // ========================================================================
3428
3722
  // Capabilities & Backend Info
3429
3723
  // ========================================================================