@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 +3 -2
- package/dist/cli/pyx-mem.mjs +1 -1
- package/dist/data-plane-contract.mjs +49 -21
- package/dist/index.d.ts +10 -0
- package/package.json +1 -1
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
|
|
60
|
-
|
|
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';
|
package/dist/cli/pyx-mem.mjs
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
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
|
|
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;
|