@pyxmate/memory 1.16.3 → 1.17.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/README.md CHANGED
@@ -56,8 +56,9 @@ for the full CLI contract and exit codes.
56
56
  ## Use it programmatically
57
57
 
58
58
  `MemoryClient` connects directly to a pyx-memory instance server (self-hosted
59
- or a sidecar you run). Hosted pyx-memory access uses MCP via `pyx-mem mcp` or
60
- the `/mcp` endpoint not this HTTP client.
59
+ or a sidecar you run, e.g. `docker run -p 7822:7822 -v memory-data:/data
60
+ ghcr.io/pyx-corp/pyx-memory-v1:latest`). Hosted pyx-memory access uses MCP via
61
+ `pyx-mem mcp` or the `/mcp` endpoint — not this HTTP client.
61
62
 
62
63
  ```ts
63
64
  import { MemoryClient } from '@pyxmate/memory';
@@ -835,7 +835,7 @@ function createProxyServer(client, version, uploadLocalFile) {
835
835
  return server;
836
836
  }
837
837
  async function runMcpProxyServer(opts) {
838
- const version = opts.version ?? (true ? "1.16.3" : "0.0.0-dev");
838
+ const version = opts.version ?? (true ? "1.17.0" : "0.0.0-dev");
839
839
  const read = await opts.readCredentials();
840
840
  if (!read.ok) {
841
841
  const text = read.result.content.map((c) => c.type === "text" ? c.text : "").join(" ").trim();
@@ -346,6 +346,18 @@ function normalizeGet(input) {
346
346
  id: requireString(value.id, "input.id", { maxBytes: DATA_PLANE_COMPILER_LIMITS.identityBytes })
347
347
  };
348
348
  }
349
+ var CALLER_ENTRY_ID_RESERVED_PREFIXES = ["synthesis:", "user-profile:", "correction:"];
350
+ function optionalCallerEntryId(value, field) {
351
+ if (value === void 0) return void 0;
352
+ const id = requireString(value, field, { maxBytes: DATA_PLANE_COMPILER_LIMITS.identityBytes });
353
+ if (/\p{Cc}/u.test(id)) throw new Error(`${field} must not contain control characters`);
354
+ for (const prefix of CALLER_ENTRY_ID_RESERVED_PREFIXES) {
355
+ if (id.startsWith(prefix)) {
356
+ throw new Error(`${field} must not use the platform-reserved '${prefix}' id prefix`);
357
+ }
358
+ }
359
+ return id;
360
+ }
349
361
  function normalizeMcpDelete(input) {
350
362
  const value = requireObject(input, "input");
351
363
  requireExactKeys(value, ["id", "reason"], "input");
@@ -356,14 +368,6 @@ function normalizeMcpDelete(input) {
356
368
  reason: requireString(value.reason, "input.reason")
357
369
  };
358
370
  }
359
- function normalizeRestGet(input) {
360
- const normalized = normalizeGet(input);
361
- const id = normalized.id;
362
- if (typeof id !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)) {
363
- throw new Error("input.id must be a valid UUID");
364
- }
365
- return normalized;
366
- }
367
371
  function normalizeMcpListEntries(value, normalized) {
368
372
  if (value.since !== void 0) requireString(value.since, "input.since", { nonempty: false });
369
373
  withOptional(normalized, "status", optionalEnum(value.status, ENTRY_STATUSES, "input.status"));
@@ -513,9 +517,29 @@ function normalizeGraphRelationships(value) {
513
517
  return normalized;
514
518
  });
515
519
  }
520
+ var MCP_SERVER_OWNED_METADATA_KEYS = ["source", "topic", "project", "_kind"];
521
+ function normalizeMcpStoreMetadata(value) {
522
+ const serverOwned = {
523
+ source: "agent",
524
+ topic: requireString(value.topic, "input.topic"),
525
+ project: requireString(value.project, "input.project")
526
+ };
527
+ if (value.metadata === void 0) return serverOwned;
528
+ const caller = canonicalValue(
529
+ requireObject(value.metadata, "input.metadata"),
530
+ "input.metadata"
531
+ );
532
+ for (const key of MCP_SERVER_OWNED_METADATA_KEYS) {
533
+ if (key in caller) {
534
+ throw new Error(`input.metadata.${key} is server-owned and cannot be caller-supplied`);
535
+ }
536
+ }
537
+ return { ...caller, ...serverOwned };
538
+ }
516
539
  function normalizeStore(input, context, mcp) {
517
540
  const value = requireObject(input, "input");
518
541
  const common = [
542
+ "id",
519
543
  "content",
520
544
  "type",
521
545
  "targets",
@@ -532,7 +556,7 @@ function normalizeStore(input, context, mcp) {
532
556
  ];
533
557
  requireExactKeys(
534
558
  value,
535
- mcp ? [...common, "topic", "project", "triples", "entitiesOnly"] : [...common, "metadata"],
559
+ mcp ? [...common, "topic", "project", "metadata", "triples", "entitiesOnly"] : [...common, "metadata"],
536
560
  "input"
537
561
  );
538
562
  if (value.namespaceId !== void 0 && value.namespaceId !== context.scope.namespaceId) {
@@ -545,15 +569,12 @@ function normalizeStore(input, context, mcp) {
545
569
  content: requireString(value.content, "input.content"),
546
570
  type: optionalEnum(value.type, MEMORY_TYPES, "input.type") ?? "long-term",
547
571
  targets: normalizeStoreTargets(value.targets),
548
- metadata: mcp ? {
549
- source: "agent",
550
- topic: requireString(value.topic, "input.topic"),
551
- project: requireString(value.project, "input.project")
552
- } : canonicalValue(value.metadata ?? {}, "input.metadata"),
572
+ metadata: mcp ? normalizeMcpStoreMetadata(value) : canonicalValue(value.metadata ?? {}, "input.metadata"),
553
573
  createdAt: context.resolvedAt,
554
574
  ingestTime: context.resolvedAt,
555
575
  extractEntities: false
556
576
  };
577
+ withOptional(normalized, "id", optionalCallerEntryId(value.id, "input.id"));
557
578
  withOptional(normalized, "source", optionalNonemptyString(value.source, "input.source"));
558
579
  withOptional(normalized, "agentId", optionalNonemptyString(value.agentId, "input.agentId"));
559
580
  withOptional(normalized, "sessionId", optionalNonemptyString(value.sessionId, "input.sessionId"));
@@ -638,12 +659,19 @@ function normalizeBatchStore(input, context) {
638
659
  if (!Array.isArray(value.entries) || value.entries.length < 1 || value.entries.length > 100) {
639
660
  throw new Error("input.entries must contain 1-100 entries");
640
661
  }
641
- return {
642
- entries: value.entries.map((entry, batchIndex) => ({
643
- ...normalizeStore(entry, context, false),
644
- batchIndex
645
- }))
646
- };
662
+ const entries = value.entries.map((entry, batchIndex) => ({
663
+ ...normalizeStore(entry, context, false),
664
+ batchIndex
665
+ }));
666
+ const callerIds = /* @__PURE__ */ new Set();
667
+ for (const entry of entries) {
668
+ if (typeof entry.id !== "string") continue;
669
+ if (callerIds.has(entry.id)) {
670
+ throw new Error(`input.entries contains duplicate entry id: ${entry.id}`);
671
+ }
672
+ callerIds.add(entry.id);
673
+ }
674
+ return { entries };
647
675
  }
648
676
  function normalizeSearch(input, mcp) {
649
677
  const value = requireObject(input, "input");
@@ -856,7 +884,7 @@ function normalizeSupportedInput(operation, input, context) {
856
884
  case "rest:list":
857
885
  return normalizeRestList(input);
858
886
  case "rest:get":
859
- return normalizeRestGet(input);
887
+ return normalizeGet(input);
860
888
  case "mcp:get_memory":
861
889
  return normalizeGet(input);
862
890
  case "mcp:list_memories":
package/dist/index.d.ts CHANGED
@@ -722,6 +722,16 @@ interface MemoryEntry {
722
722
  * to NULL (never resurrected to active) if the successor is later forgotten.
723
723
  */
724
724
  supersededByEntryId?: string;
725
+ /**
726
+ * @internal Hosted data-plane only. SHA-256 of the canonical caller store
727
+ * payload (server-stamped timestamps excluded), persisted for caller-id
728
+ * stores so a re-send of the same `metadata.dbRef.revision` can be
729
+ * classified as an idempotent no-op (digest match) or a
730
+ * `revision_reuse_conflict` (digest mismatch). Graph entities/relationships
731
+ * are part of the payload but not of the row, so the digest cannot be
732
+ * recomputed from stored state.
733
+ */
734
+ payloadDigest?: string;
725
735
  }
726
736
  interface MemorySearchParams {
727
737
  query: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyxmate/memory",
3
- "version": "1.16.3",
3
+ "version": "1.17.0",
4
4
  "type": "module",
5
5
  "description": "SDK for pyx-memory — Memory as a Service for AI agents",
6
6
  "license": "MIT",