@pyxmate/memory 1.2.0 → 1.3.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/{chunk-3SDKJ5TB.mjs → chunk-BS6K64SA.mjs} +140 -4
- package/dist/{chunk-3PBLTOBR.mjs → chunk-MZF55IUR.mjs} +1 -1
- package/dist/cli/pyx-mem.mjs +33 -800
- package/dist/dashboard.mjs +2 -3
- package/dist/index.mjs +4 -6
- package/dist/react.mjs +2 -3
- package/package.json +1 -1
- package/dist/chunk-X6AYWXW7.mjs +0 -143
|
@@ -1,7 +1,129 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
// ../shared/src/constants/defaults.ts
|
|
2
|
+
var DEFAULTS = {
|
|
3
|
+
DATA_DIR: "./data",
|
|
4
|
+
VECTOR_PROVIDER: "lancedb",
|
|
5
|
+
MEMORY_SERVER_PORT: 7822
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// ../shared/src/graph/extraction.ts
|
|
9
|
+
function normalizeGraphLabel(value, fallback) {
|
|
10
|
+
const normalized = value.trim().toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
11
|
+
return normalized.length > 0 ? normalized : fallback;
|
|
12
|
+
}
|
|
13
|
+
function normalizeNameKey(name) {
|
|
14
|
+
return name.trim().toLowerCase().replace(/\s+/g, " ");
|
|
15
|
+
}
|
|
16
|
+
function relationshipKey(relationship) {
|
|
17
|
+
return [
|
|
18
|
+
relationship.source.trim().toLowerCase(),
|
|
19
|
+
relationship.target.trim().toLowerCase(),
|
|
20
|
+
normalizeGraphLabel(relationship.type, "RELATED_TO")
|
|
21
|
+
].join("\0");
|
|
22
|
+
}
|
|
23
|
+
function mergeExtractedEntities(callerEntities, callerRelationships, extracted) {
|
|
24
|
+
const entities = [...callerEntities ?? []];
|
|
25
|
+
const relationships = [...callerRelationships ?? []];
|
|
26
|
+
const nameByLowercase = /* @__PURE__ */ new Map();
|
|
27
|
+
for (const entity of entities) {
|
|
28
|
+
const key = entity.name.toLowerCase();
|
|
29
|
+
if (!nameByLowercase.has(key)) nameByLowercase.set(key, entity.name);
|
|
30
|
+
}
|
|
31
|
+
for (const entity of extracted.entities) {
|
|
32
|
+
const key = entity.name.toLowerCase();
|
|
33
|
+
if (nameByLowercase.has(key)) continue;
|
|
34
|
+
entities.push({ ...entity, type: normalizeGraphLabel(entity.type, "CONCEPT") });
|
|
35
|
+
nameByLowercase.set(key, entity.name);
|
|
36
|
+
}
|
|
37
|
+
for (const relationship of extracted.relations) {
|
|
38
|
+
const source = nameByLowercase.get(relationship.source.toLowerCase());
|
|
39
|
+
const target = nameByLowercase.get(relationship.target.toLowerCase());
|
|
40
|
+
if (source && target) {
|
|
41
|
+
relationships.push({
|
|
42
|
+
...relationship,
|
|
43
|
+
source,
|
|
44
|
+
target,
|
|
45
|
+
type: normalizeGraphLabel(relationship.type, "RELATED_TO")
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const seenRelationships = /* @__PURE__ */ new Set();
|
|
50
|
+
const dedupedRelationships = [];
|
|
51
|
+
for (const relationship of relationships) {
|
|
52
|
+
const key = relationshipKey(relationship);
|
|
53
|
+
if (seenRelationships.has(key)) continue;
|
|
54
|
+
seenRelationships.add(key);
|
|
55
|
+
dedupedRelationships.push(relationship);
|
|
56
|
+
}
|
|
57
|
+
return { entities, relationships: dedupedRelationships };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ../shared/src/types/isolation.ts
|
|
61
|
+
var NamespaceIsolation = {
|
|
62
|
+
SHARED: "shared",
|
|
63
|
+
STRICT: "strict"
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// ../shared/src/types/memory.ts
|
|
67
|
+
var MemoryType = {
|
|
68
|
+
SHORT_TERM: "short-term",
|
|
69
|
+
LONG_TERM: "long-term",
|
|
70
|
+
WORKING: "working",
|
|
71
|
+
EPISODIC: "episodic",
|
|
72
|
+
SUMMARY: "summary"
|
|
73
|
+
};
|
|
74
|
+
var SensitivityLevel = {
|
|
75
|
+
PUBLIC: "public",
|
|
76
|
+
INTERNAL: "internal",
|
|
77
|
+
SECRET: "secret"
|
|
78
|
+
};
|
|
79
|
+
var RAGStrategy = {
|
|
80
|
+
NAIVE: "naive",
|
|
81
|
+
GRAPH: "graph",
|
|
82
|
+
HYBRID: "hybrid"
|
|
83
|
+
};
|
|
84
|
+
var DEPRECATED_RAG_STRATEGIES = /* @__PURE__ */ new Map([
|
|
85
|
+
["agentic", "strategy.deprecated:agentic \u2014 removed in v0.26, use hybrid"]
|
|
86
|
+
]);
|
|
87
|
+
var VectorProvider = {
|
|
88
|
+
LANCEDB: "lancedb"
|
|
89
|
+
};
|
|
90
|
+
var EmbeddingProviderName = {
|
|
91
|
+
STUB: "stub",
|
|
92
|
+
/** @deprecated Vestigial — pyx-memory uses internal EmbeddingGemma embeddings. */
|
|
93
|
+
ANTHROPIC: "anthropic",
|
|
94
|
+
/** @deprecated Vestigial — pyx-memory uses internal EmbeddingGemma embeddings. */
|
|
95
|
+
OPENAI: "openai",
|
|
96
|
+
/** In-process ONNX model (default: EmbeddingGemma-300M). */
|
|
97
|
+
LOCAL: "local",
|
|
98
|
+
/** Remote OpenAI-compatible embedding service (pyx-cloud shared, custom, etc.). */
|
|
99
|
+
HTTP: "http"
|
|
100
|
+
};
|
|
101
|
+
var StoreTarget = {
|
|
102
|
+
SQLITE: "sqlite",
|
|
103
|
+
VECTOR: "vector",
|
|
104
|
+
GRAPH: "graph"
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// ../shared/src/types/move.ts
|
|
108
|
+
var MoveFailureReason = {
|
|
109
|
+
/** Entry not found in the caller's tenant. */
|
|
110
|
+
NOT_FOUND: "not_found",
|
|
111
|
+
/** Move would cross tenant boundary (always forbidden). */
|
|
112
|
+
CROSS_TENANT_FORBIDDEN: "cross_tenant_forbidden",
|
|
113
|
+
/** Target namespace ID does not exist in the caller's tenant. */
|
|
114
|
+
TARGET_NAMESPACE_NOT_FOUND: "target_namespace_not_found",
|
|
115
|
+
/** SQLite metadata update failed; no compensation needed. */
|
|
116
|
+
SQLITE_UPDATE_FAILED: "sqlite_update_failed",
|
|
117
|
+
/** Vector store metadata update failed; SQLite reverted. */
|
|
118
|
+
VECTOR_UPDATE_FAILED: "vector_update_failed",
|
|
119
|
+
/** Graph edge namespace update failed; SQLite + vector reverted. */
|
|
120
|
+
GRAPH_UPDATE_FAILED: "graph_update_failed",
|
|
121
|
+
/** Compensation itself failed — manual intervention required. */
|
|
122
|
+
COMPENSATION_FAILED: "compensation_failed"
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// ../shared/src/types/principal.ts
|
|
126
|
+
var SINGLE_TENANT_ID = "_single";
|
|
5
127
|
|
|
6
128
|
// ../client/src/disabled-memory.ts
|
|
7
129
|
var DEFAULT_PAGE_LIMIT = 20;
|
|
@@ -850,6 +972,20 @@ var MemoryClient = class {
|
|
|
850
972
|
};
|
|
851
973
|
|
|
852
974
|
export {
|
|
975
|
+
DEFAULTS,
|
|
976
|
+
normalizeGraphLabel,
|
|
977
|
+
normalizeNameKey,
|
|
978
|
+
mergeExtractedEntities,
|
|
979
|
+
NamespaceIsolation,
|
|
980
|
+
MemoryType,
|
|
981
|
+
SensitivityLevel,
|
|
982
|
+
RAGStrategy,
|
|
983
|
+
DEPRECATED_RAG_STRATEGIES,
|
|
984
|
+
VectorProvider,
|
|
985
|
+
EmbeddingProviderName,
|
|
986
|
+
StoreTarget,
|
|
987
|
+
MoveFailureReason,
|
|
988
|
+
SINGLE_TENANT_ID,
|
|
853
989
|
DisabledMemory,
|
|
854
990
|
MemoryServerError,
|
|
855
991
|
MemoryClient
|
package/dist/cli/pyx-mem.mjs
CHANGED
|
@@ -1,21 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
SEARCH_ENUMERATION_CONCEPT_DESC,
|
|
5
|
-
SEARCH_LIMIT_DESC,
|
|
6
|
-
STORE_ENTITIES_DESC,
|
|
7
|
-
STORE_EVENT_TIME_DESC,
|
|
8
|
-
STORE_RELATIONSHIPS_DESC,
|
|
9
|
-
STORE_TOOL_DESC,
|
|
10
|
-
STORE_TRIPLES_DESC,
|
|
11
|
-
STRUCTURE_GRAPH_PROMPT_DESC,
|
|
12
|
-
buildDesignGuide,
|
|
13
|
-
buildGraphStructuringPrompt
|
|
3
|
+
buildDesignGuide
|
|
14
4
|
} from "../chunk-XHEVB23R.mjs";
|
|
15
|
-
import {
|
|
16
|
-
normalizeGraphLabel,
|
|
17
|
-
normalizeNameKey
|
|
18
|
-
} from "../chunk-X6AYWXW7.mjs";
|
|
19
5
|
|
|
20
6
|
// src/cli/exit-codes.ts
|
|
21
7
|
var EXIT = {
|
|
@@ -352,7 +338,7 @@ async function promptMasked(label) {
|
|
|
352
338
|
`);
|
|
353
339
|
return value;
|
|
354
340
|
}
|
|
355
|
-
return new Promise((
|
|
341
|
+
return new Promise((resolve3, reject) => {
|
|
356
342
|
const previousEncoding = stdin.readableEncoding;
|
|
357
343
|
stdin.setEncoding("utf8");
|
|
358
344
|
stdin.setRawMode(true);
|
|
@@ -370,7 +356,7 @@ async function promptMasked(label) {
|
|
|
370
356
|
if (ch === "\n" || ch === "\r") {
|
|
371
357
|
cleanup();
|
|
372
358
|
stdout.write("\n");
|
|
373
|
-
|
|
359
|
+
resolve3(buf.trim());
|
|
374
360
|
return;
|
|
375
361
|
}
|
|
376
362
|
if (ch === "") {
|
|
@@ -811,7 +797,7 @@ function createProxyServer(client, version, uploadLocalFile) {
|
|
|
811
797
|
return server;
|
|
812
798
|
}
|
|
813
799
|
async function runMcpProxyServer(opts) {
|
|
814
|
-
const version = opts.version ?? (true ? "1.
|
|
800
|
+
const version = opts.version ?? (true ? "1.3.0" : "0.0.0-dev");
|
|
815
801
|
const read = await opts.readCredentials();
|
|
816
802
|
if (!read.ok) {
|
|
817
803
|
const text = read.result.content.map((c) => c.type === "text" ? c.text : "").join(" ").trim();
|
|
@@ -828,14 +814,14 @@ async function runMcpProxyServer(opts) {
|
|
|
828
814
|
} catch (err) {
|
|
829
815
|
const msg = err instanceof Error ? err.message : String(err);
|
|
830
816
|
throw new Error(
|
|
831
|
-
`Could not reach the pyx-memory remote MCP at ${url.href}: ${msg}. Verify the endpoint and key with \`pyx-mem doctor
|
|
817
|
+
`Could not reach the pyx-memory remote MCP at ${url.href}: ${msg}. Verify the endpoint and key with \`pyx-mem doctor\`.`
|
|
832
818
|
);
|
|
833
819
|
}
|
|
834
820
|
try {
|
|
835
821
|
const server = createProxyServer(client, version, createLocalFileUploader(read.credentials));
|
|
836
822
|
const transport = new StdioServerTransport();
|
|
837
|
-
const closed = new Promise((
|
|
838
|
-
transport.onclose = () =>
|
|
823
|
+
const closed = new Promise((resolve3) => {
|
|
824
|
+
transport.onclose = () => resolve3();
|
|
839
825
|
});
|
|
840
826
|
await server.connect(transport);
|
|
841
827
|
await closed;
|
|
@@ -845,765 +831,15 @@ async function runMcpProxyServer(opts) {
|
|
|
845
831
|
}
|
|
846
832
|
}
|
|
847
833
|
|
|
848
|
-
// src/mcp/server.ts
|
|
849
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
850
|
-
import { StdioServerTransport as StdioServerTransport2 } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
851
|
-
|
|
852
|
-
// src/mcp/prompts/index.ts
|
|
853
|
-
import { z } from "zod";
|
|
854
|
-
var structureGraphPrompt = {
|
|
855
|
-
name: "structure_graph",
|
|
856
|
-
description: STRUCTURE_GRAPH_PROMPT_DESC,
|
|
857
|
-
register(server) {
|
|
858
|
-
server.registerPrompt(
|
|
859
|
-
"structure_graph",
|
|
860
|
-
{
|
|
861
|
-
title: "Structure content into a knowledge graph",
|
|
862
|
-
description: STRUCTURE_GRAPH_PROMPT_DESC,
|
|
863
|
-
argsSchema: {
|
|
864
|
-
content: z.string().min(1).describe("The memory content to extract graph fields from.")
|
|
865
|
-
}
|
|
866
|
-
},
|
|
867
|
-
({ content }) => ({
|
|
868
|
-
messages: [
|
|
869
|
-
{ role: "user", content: { type: "text", text: buildGraphStructuringPrompt(content) } }
|
|
870
|
-
]
|
|
871
|
-
})
|
|
872
|
-
);
|
|
873
|
-
}
|
|
874
|
-
};
|
|
875
|
-
var ALL_PROMPTS = [structureGraphPrompt];
|
|
876
|
-
|
|
877
|
-
// src/mcp/tools/corrections.ts
|
|
878
|
-
import { z as z3 } from "zod";
|
|
879
|
-
|
|
880
|
-
// src/mcp/tools/scopes.ts
|
|
881
|
-
import { z as z2 } from "zod";
|
|
882
|
-
var scopeShape = {
|
|
883
|
-
tenantId: z2.string().optional().describe("Sent as X-Tenant-Id for multi-tenant isolation."),
|
|
884
|
-
userId: z2.string().optional().describe("Sent as X-User-Id."),
|
|
885
|
-
teamId: z2.string().optional().describe("Sent as X-Team-Id."),
|
|
886
|
-
callerAccessLevel: z2.enum(["public", "internal", "secret"]).optional().describe("Sent as X-Caller-Access-Level for sensitivity filtering.")
|
|
887
|
-
};
|
|
888
|
-
|
|
889
|
-
// src/mcp/tools/corrections.ts
|
|
890
|
-
var recordInputShape = {
|
|
891
|
-
namespaceId: z3.string().min(1).optional().describe("Namespace id to record into. Omit in single-tenant deployments (namespace-free)."),
|
|
892
|
-
whatWasWrong: z3.string().min(1).describe("What the agent did wrong."),
|
|
893
|
-
whatToDoInstead: z3.string().min(1).describe("The corrective instruction to follow instead."),
|
|
894
|
-
appliesWhen: z3.string().min(1).describe("When this correction applies \u2014 the task context future tasks are matched against."),
|
|
895
|
-
project: z3.string().optional().describe("Optional project scope; omit to apply to all projects."),
|
|
896
|
-
taskShape: z3.string().optional().describe("Optional task-shape hint stored for provenance."),
|
|
897
|
-
...scopeShape
|
|
898
|
-
};
|
|
899
|
-
var recordCorrectionTool = {
|
|
900
|
-
name: "record_correction",
|
|
901
|
-
config: {
|
|
902
|
-
title: "Record a pyx-memory correction",
|
|
903
|
-
description: 'Record an explicit user correction so the same mistake can be avoided next time. Call this when the user explicitly corrects the agent ("you did X wrong; do Y instead"). HTTP proxy to POST /api/memory/corrections \u2014 returns `{id, createdAt}`.',
|
|
904
|
-
inputSchema: recordInputShape,
|
|
905
|
-
annotations: { readOnlyHint: false, openWorldHint: true }
|
|
906
|
-
},
|
|
907
|
-
handler: (deps) => async (raw) => {
|
|
908
|
-
const args = raw;
|
|
909
|
-
const creds = await deps.readCredentials();
|
|
910
|
-
if (!creds.ok) return creds.result;
|
|
911
|
-
const http = createHttpClient(creds.credentials, deps.fetchImpl);
|
|
912
|
-
const res = await http.requestJson({
|
|
913
|
-
method: "POST",
|
|
914
|
-
path: "/api/memory/corrections",
|
|
915
|
-
body: {
|
|
916
|
-
...args.namespaceId !== void 0 ? { namespaceId: args.namespaceId } : {},
|
|
917
|
-
whatWasWrong: args.whatWasWrong,
|
|
918
|
-
whatToDoInstead: args.whatToDoInstead,
|
|
919
|
-
appliesWhen: args.appliesWhen,
|
|
920
|
-
...args.project !== void 0 ? { project: args.project } : {},
|
|
921
|
-
...args.taskShape !== void 0 ? { taskShape: args.taskShape } : {}
|
|
922
|
-
},
|
|
923
|
-
scope: args
|
|
924
|
-
});
|
|
925
|
-
return res.ok ? mcpJson(res.data) : res.result;
|
|
926
|
-
}
|
|
927
|
-
};
|
|
928
|
-
var fetchInputShape = {
|
|
929
|
-
namespaceId: z3.string().min(1).optional().describe("Namespace id whose corrections to fetch. Omit in single-tenant deployments."),
|
|
930
|
-
taskShape: z3.string().min(1).describe("Shape of the task about to run \u2014 scored against each correction."),
|
|
931
|
-
project: z3.string().optional().describe("Optional project filter."),
|
|
932
|
-
limit: z3.number().int().positive().optional().describe("Max corrections to return (cap 5)."),
|
|
933
|
-
...scopeShape
|
|
934
|
-
};
|
|
935
|
-
var fetchApplicableCorrectionsTool = {
|
|
936
|
-
name: "fetch_applicable_corrections",
|
|
937
|
-
config: {
|
|
938
|
-
title: "Fetch applicable pyx-memory corrections",
|
|
939
|
-
description: "Fetch the corrections applicable to a task shape (\u22645, ranked by overlap then recency). pyx never auto-prepends \u2014 you decide whether to follow them. HTTP proxy to GET /api/memory/corrections \u2014 returns a `CorrectionRecord[]` (`[]` when none apply).",
|
|
940
|
-
inputSchema: fetchInputShape,
|
|
941
|
-
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
942
|
-
},
|
|
943
|
-
handler: (deps) => async (raw) => {
|
|
944
|
-
const args = raw;
|
|
945
|
-
const creds = await deps.readCredentials();
|
|
946
|
-
if (!creds.ok) return creds.result;
|
|
947
|
-
const http = createHttpClient(creds.credentials, deps.fetchImpl);
|
|
948
|
-
const res = await http.requestJson({
|
|
949
|
-
method: "GET",
|
|
950
|
-
path: "/api/memory/corrections",
|
|
951
|
-
query: {
|
|
952
|
-
namespaceId: args.namespaceId,
|
|
953
|
-
taskShape: args.taskShape,
|
|
954
|
-
project: args.project,
|
|
955
|
-
limit: args.limit
|
|
956
|
-
},
|
|
957
|
-
scope: args
|
|
958
|
-
});
|
|
959
|
-
return res.ok ? mcpJson(res.data) : res.result;
|
|
960
|
-
}
|
|
961
|
-
};
|
|
962
|
-
|
|
963
|
-
// src/mcp/tools/delete.ts
|
|
964
|
-
import { z as z4 } from "zod";
|
|
965
|
-
var inputShape = {
|
|
966
|
-
id: z4.string().min(1).describe("Required memory entry id to delete."),
|
|
967
|
-
reason: z4.string().min(1).describe("Required reason for deletion (audit)."),
|
|
968
|
-
...scopeShape
|
|
969
|
-
};
|
|
970
|
-
var deleteMemoryTool = {
|
|
971
|
-
name: "delete_memory",
|
|
972
|
-
config: {
|
|
973
|
-
title: "Delete pyx-memory entry",
|
|
974
|
-
description: "Delete one memory entry by id with an explicit reason. Self-hosted instances require an admin key; cloud delegates to tenant policy.",
|
|
975
|
-
inputSchema: inputShape,
|
|
976
|
-
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true }
|
|
977
|
-
},
|
|
978
|
-
handler: (deps) => async (raw) => {
|
|
979
|
-
const args = raw;
|
|
980
|
-
const creds = await deps.readCredentials();
|
|
981
|
-
if (!creds.ok) return creds.result;
|
|
982
|
-
const http = createHttpClient(creds.credentials, deps.fetchImpl);
|
|
983
|
-
const res = await http.requestJson({
|
|
984
|
-
method: "DELETE",
|
|
985
|
-
path: `/api/memory/entries/${encodeURIComponent(args.id)}`,
|
|
986
|
-
query: { reason: args.reason },
|
|
987
|
-
scope: args
|
|
988
|
-
});
|
|
989
|
-
return res.ok ? mcpJson({ deleted: args.id }) : res.result;
|
|
990
|
-
}
|
|
991
|
-
};
|
|
992
|
-
|
|
993
|
-
// src/mcp/tools/get.ts
|
|
994
|
-
import { z as z5 } from "zod";
|
|
995
|
-
var inputShape2 = {
|
|
996
|
-
id: z5.string().min(1).describe("Required memory entry id."),
|
|
997
|
-
...scopeShape
|
|
998
|
-
};
|
|
999
|
-
var getMemoryTool = {
|
|
1000
|
-
name: "get_memory",
|
|
1001
|
-
config: {
|
|
1002
|
-
title: "Get pyx-memory entry",
|
|
1003
|
-
description: "Fetch one memory entry by id. Multi-tenant headers and X-Caller-Access-Level are honored for sensitivity redaction.",
|
|
1004
|
-
inputSchema: inputShape2,
|
|
1005
|
-
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
1006
|
-
},
|
|
1007
|
-
handler: (deps) => async (raw) => {
|
|
1008
|
-
const args = raw;
|
|
1009
|
-
const creds = await deps.readCredentials();
|
|
1010
|
-
if (!creds.ok) return creds.result;
|
|
1011
|
-
const http = createHttpClient(creds.credentials, deps.fetchImpl);
|
|
1012
|
-
const res = await http.requestJson({
|
|
1013
|
-
method: "GET",
|
|
1014
|
-
path: `/api/memory/entries/${encodeURIComponent(args.id)}`,
|
|
1015
|
-
scope: args
|
|
1016
|
-
});
|
|
1017
|
-
return res.ok ? mcpJson(res.data) : res.result;
|
|
1018
|
-
}
|
|
1019
|
-
};
|
|
1020
|
-
|
|
1021
|
-
// src/mcp/tools/ingest.ts
|
|
1022
|
-
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
1023
|
-
import { basename as basename2, extname, isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
|
|
1024
|
-
import { z as z6 } from "zod";
|
|
1025
|
-
var IMAGE_EXT = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".webp", ".gif", ".bmp", ".tiff", ".svg"]);
|
|
1026
|
-
var MAX_BYTES = 50 * 1024 * 1024;
|
|
1027
|
-
var INGEST_ENDPOINT = "/api/memory/ingest/file";
|
|
1028
|
-
var inputShape3 = {
|
|
1029
|
-
path: z6.string().min(1).describe(
|
|
1030
|
-
"Local file path readable by the pyx-mem process. Uploaded as multipart `file`. Images require `description`; documents auto-extract text."
|
|
1031
|
-
),
|
|
1032
|
-
description: z6.string().optional().describe(
|
|
1033
|
-
"REQUIRED for images so the entry is semantically searchable. Optional for documents with extractable text."
|
|
1034
|
-
),
|
|
1035
|
-
namespaceId: z6.string().optional().describe("Optional ReBAC namespace for entries created from this file."),
|
|
1036
|
-
...scopeShape
|
|
1037
|
-
};
|
|
1038
|
-
var ingestMemoryFileTool = {
|
|
1039
|
-
name: "ingest_memory_file",
|
|
1040
|
-
config: {
|
|
1041
|
-
title: "Ingest file into pyx-memory",
|
|
1042
|
-
description: "Upload a document or image into pyx-memory. Images REQUIRE description. Large docx/xlsx/pptx should be pre-extracted to txt/md. 50MB cap.",
|
|
1043
|
-
inputSchema: inputShape3,
|
|
1044
|
-
annotations: { readOnlyHint: false, idempotentHint: false, openWorldHint: true }
|
|
1045
|
-
},
|
|
1046
|
-
// `path` is a host-local file. On the remote `/mcp` route the server cannot
|
|
1047
|
-
// read it, so this descriptor lets the local proxy bridge the upload (read
|
|
1048
|
-
// bytes here, multipart-POST to the declared endpoint). The bundled handler
|
|
1049
|
-
// below ignores it. Field names map 1:1 to the multipart form built below.
|
|
1050
|
-
localUpload: {
|
|
1051
|
-
pathArg: "path",
|
|
1052
|
-
endpoint: INGEST_ENDPOINT,
|
|
1053
|
-
fileField: "file",
|
|
1054
|
-
optionalArgs: ["description", "namespaceId"],
|
|
1055
|
-
maxBytes: MAX_BYTES
|
|
1056
|
-
},
|
|
1057
|
-
handler: (deps) => async (raw) => {
|
|
1058
|
-
const args = raw;
|
|
1059
|
-
const ext = extname(args.path).toLowerCase();
|
|
1060
|
-
const isImage = IMAGE_EXT.has(ext);
|
|
1061
|
-
if (isImage && !args.description?.trim()) {
|
|
1062
|
-
return mcpText(
|
|
1063
|
-
`Image ingest requires a non-empty \`description\`. Provide a 1-2 sentence summary of the image content so it becomes searchable.`,
|
|
1064
|
-
true
|
|
1065
|
-
);
|
|
1066
|
-
}
|
|
1067
|
-
const abs = isAbsolute2(args.path) ? args.path : resolve2(args.path);
|
|
1068
|
-
let bytes;
|
|
1069
|
-
try {
|
|
1070
|
-
const info = await stat2(abs);
|
|
1071
|
-
if (info.size > MAX_BYTES) {
|
|
1072
|
-
return mcpText(`File too large: ${info.size} bytes > 50MB cap.`, true);
|
|
1073
|
-
}
|
|
1074
|
-
bytes = await readFile2(abs);
|
|
1075
|
-
} catch (err) {
|
|
1076
|
-
return mcpText(
|
|
1077
|
-
`Failed to read ${abs}: ${err instanceof Error ? err.message : String(err)}.`,
|
|
1078
|
-
true
|
|
1079
|
-
);
|
|
1080
|
-
}
|
|
1081
|
-
const creds = await deps.readCredentials();
|
|
1082
|
-
if (!creds.ok) return creds.result;
|
|
1083
|
-
const http = createHttpClient(creds.credentials, deps.fetchImpl);
|
|
1084
|
-
const res = await http.requestMultipart({
|
|
1085
|
-
path: INGEST_ENDPOINT,
|
|
1086
|
-
scope: args,
|
|
1087
|
-
formData: () => {
|
|
1088
|
-
const form = new FormData();
|
|
1089
|
-
const file = new File([new Uint8Array(bytes)], basename2(abs));
|
|
1090
|
-
form.set("file", file);
|
|
1091
|
-
if (args.description) form.set("description", args.description);
|
|
1092
|
-
if (args.namespaceId) form.set("namespaceId", args.namespaceId);
|
|
1093
|
-
return form;
|
|
1094
|
-
}
|
|
1095
|
-
});
|
|
1096
|
-
return res.ok ? mcpJson(res.data) : res.result;
|
|
1097
|
-
}
|
|
1098
|
-
};
|
|
1099
|
-
|
|
1100
|
-
// src/mcp/tools/lineage.ts
|
|
1101
|
-
import { z as z7 } from "zod";
|
|
1102
|
-
var inputShape4 = {
|
|
1103
|
-
subject: z7.string().optional().describe("Graph subject to trace, used with relation for graph lineage."),
|
|
1104
|
-
relation: z7.string().optional().describe("Graph relation to trace for the subject."),
|
|
1105
|
-
entryId: z7.string().optional().describe("Memory entry id to trace through its supersededBy chain."),
|
|
1106
|
-
asOf: z7.string().optional().describe("Only include lineage versions ingested by this ISO-8601 time."),
|
|
1107
|
-
eventTimeStart: z7.string().optional().describe("Inclusive event-time start (ISO-8601)."),
|
|
1108
|
-
eventTimeEnd: z7.string().optional().describe("Inclusive event-time end (ISO-8601)."),
|
|
1109
|
-
beforeValue: z7.string().optional().describe("Stop before the lineage version with this value."),
|
|
1110
|
-
limit: z7.number().int().positive().optional().describe("Maximum lineage versions to return."),
|
|
1111
|
-
...scopeShape
|
|
1112
|
-
};
|
|
1113
|
-
var lineageTool = {
|
|
1114
|
-
name: "lineage",
|
|
1115
|
-
config: {
|
|
1116
|
-
title: "Trace pyx-memory fact lineage",
|
|
1117
|
-
description: "Return the time-ordered history of how a fact evolved: subject+relation traces graph lineage, entryId traces a supersededBy chain. Use for questions like what did X use before Y or how did this fact evolve.",
|
|
1118
|
-
inputSchema: inputShape4,
|
|
1119
|
-
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
1120
|
-
},
|
|
1121
|
-
handler: (deps) => async (raw) => {
|
|
1122
|
-
const args = raw;
|
|
1123
|
-
const creds = await deps.readCredentials();
|
|
1124
|
-
if (!creds.ok) return creds.result;
|
|
1125
|
-
const http = createHttpClient(creds.credentials, deps.fetchImpl);
|
|
1126
|
-
const res = await http.requestJson({
|
|
1127
|
-
method: "GET",
|
|
1128
|
-
path: "/api/memory/lineage",
|
|
1129
|
-
query: {
|
|
1130
|
-
subject: args.subject,
|
|
1131
|
-
relation: args.relation,
|
|
1132
|
-
entryId: args.entryId,
|
|
1133
|
-
asOf: args.asOf,
|
|
1134
|
-
eventTimeStart: args.eventTimeStart,
|
|
1135
|
-
eventTimeEnd: args.eventTimeEnd,
|
|
1136
|
-
beforeValue: args.beforeValue,
|
|
1137
|
-
limit: args.limit
|
|
1138
|
-
},
|
|
1139
|
-
scope: args
|
|
1140
|
-
});
|
|
1141
|
-
return res.ok ? mcpJson(res.data) : res.result;
|
|
1142
|
-
}
|
|
1143
|
-
};
|
|
1144
|
-
|
|
1145
|
-
// src/mcp/tools/list.ts
|
|
1146
|
-
import { z as z8 } from "zod";
|
|
1147
|
-
var inputShape5 = {
|
|
1148
|
-
mode: z8.enum(["entries", "log"]).optional().describe(
|
|
1149
|
-
"Listing mode. `entries` (default) returns paginated entries by filter; `log` returns the chronological memory log."
|
|
1150
|
-
),
|
|
1151
|
-
page: z8.number().int().min(1).optional().describe("1-based page index for entries mode."),
|
|
1152
|
-
limit: z8.number().int().min(1).max(200).optional().describe("Page size; server clamps."),
|
|
1153
|
-
type: z8.enum(["short-term", "long-term", "working", "episodic", "summary"]).optional().describe("Filter by memory type."),
|
|
1154
|
-
agentId: z8.string().optional().describe("Filter to memories from this agentId."),
|
|
1155
|
-
since: z8.string().optional().describe("ISO-8601 lower bound for log mode."),
|
|
1156
|
-
...scopeShape
|
|
1157
|
-
};
|
|
1158
|
-
var listMemoriesTool = {
|
|
1159
|
-
name: "list_memories",
|
|
1160
|
-
config: {
|
|
1161
|
-
title: "List pyx-memory",
|
|
1162
|
-
description: "List recent memory entries or a chronological memory log. Same tool for both reads \u2014 `mode` controls which.",
|
|
1163
|
-
inputSchema: inputShape5,
|
|
1164
|
-
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
1165
|
-
},
|
|
1166
|
-
handler: (deps) => async (raw) => {
|
|
1167
|
-
const args = raw;
|
|
1168
|
-
const creds = await deps.readCredentials();
|
|
1169
|
-
if (!creds.ok) return creds.result;
|
|
1170
|
-
const http = createHttpClient(creds.credentials, deps.fetchImpl);
|
|
1171
|
-
const mode = args.mode ?? "entries";
|
|
1172
|
-
const path = mode === "log" ? "/api/memory/log" : "/api/memory/entries";
|
|
1173
|
-
const res = await http.requestJson({
|
|
1174
|
-
method: "GET",
|
|
1175
|
-
path,
|
|
1176
|
-
query: {
|
|
1177
|
-
page: args.page,
|
|
1178
|
-
limit: args.limit,
|
|
1179
|
-
type: args.type,
|
|
1180
|
-
agentId: args.agentId,
|
|
1181
|
-
since: args.since
|
|
1182
|
-
},
|
|
1183
|
-
scope: args
|
|
1184
|
-
});
|
|
1185
|
-
return res.ok ? mcpJson(res.data) : res.result;
|
|
1186
|
-
}
|
|
1187
|
-
};
|
|
1188
|
-
|
|
1189
|
-
// src/mcp/tools/profile.ts
|
|
1190
|
-
import { z as z9 } from "zod";
|
|
1191
|
-
var getInputShape = {
|
|
1192
|
-
namespaceId: z9.string().min(1).describe("Namespace id whose user-profile to fetch."),
|
|
1193
|
-
...scopeShape
|
|
1194
|
-
};
|
|
1195
|
-
var getUserProfileTool = {
|
|
1196
|
-
name: "get_user_profile",
|
|
1197
|
-
config: {
|
|
1198
|
-
title: "Get pyx-memory user profile",
|
|
1199
|
-
description: "Fetch the current user-profile snapshot for a namespace. HTTP proxy to GET /api/memory/profile/user \u2014 returns `{content, updatedAt, contentSize}` or a not-found error.",
|
|
1200
|
-
inputSchema: getInputShape,
|
|
1201
|
-
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
1202
|
-
},
|
|
1203
|
-
handler: (deps) => async (raw) => {
|
|
1204
|
-
const args = raw;
|
|
1205
|
-
const creds = await deps.readCredentials();
|
|
1206
|
-
if (!creds.ok) return creds.result;
|
|
1207
|
-
const http = createHttpClient(creds.credentials, deps.fetchImpl);
|
|
1208
|
-
const res = await http.requestJson({
|
|
1209
|
-
method: "GET",
|
|
1210
|
-
path: "/api/memory/profile/user",
|
|
1211
|
-
query: { namespaceId: args.namespaceId },
|
|
1212
|
-
scope: args
|
|
1213
|
-
});
|
|
1214
|
-
return res.ok ? mcpJson(res.data) : res.result;
|
|
1215
|
-
}
|
|
1216
|
-
};
|
|
1217
|
-
var upsertInputShape = {
|
|
1218
|
-
namespaceId: z9.string().min(1).describe("Namespace id to upsert the user-profile into."),
|
|
1219
|
-
content: z9.string().min(1).describe("Full freeform profile content (UTF-8, \u22648192 bytes; server enforces the cap)."),
|
|
1220
|
-
...scopeShape
|
|
1221
|
-
};
|
|
1222
|
-
var upsertUserProfileTool = {
|
|
1223
|
-
name: "upsert_user_profile",
|
|
1224
|
-
config: {
|
|
1225
|
-
title: "Upsert pyx-memory user profile",
|
|
1226
|
-
description: "Upsert the user-profile snapshot for a namespace. HTTP proxy to PUT /api/memory/profile/user \u2014 returns `{updatedAt, contentSize}`. Idempotent: re-upsert preserves `createdAt` and refreshes `updatedAt`.",
|
|
1227
|
-
inputSchema: upsertInputShape,
|
|
1228
|
-
annotations: { readOnlyHint: false, openWorldHint: true }
|
|
1229
|
-
},
|
|
1230
|
-
handler: (deps) => async (raw) => {
|
|
1231
|
-
const args = raw;
|
|
1232
|
-
const creds = await deps.readCredentials();
|
|
1233
|
-
if (!creds.ok) return creds.result;
|
|
1234
|
-
const http = createHttpClient(creds.credentials, deps.fetchImpl);
|
|
1235
|
-
const res = await http.requestJson({
|
|
1236
|
-
method: "PUT",
|
|
1237
|
-
path: "/api/memory/profile/user",
|
|
1238
|
-
body: { namespaceId: args.namespaceId, content: args.content },
|
|
1239
|
-
scope: args
|
|
1240
|
-
});
|
|
1241
|
-
return res.ok ? mcpJson(res.data) : res.result;
|
|
1242
|
-
}
|
|
1243
|
-
};
|
|
1244
|
-
|
|
1245
|
-
// src/mcp/tools/reinforce.ts
|
|
1246
|
-
import { z as z10 } from "zod";
|
|
1247
|
-
var inputShape6 = {
|
|
1248
|
-
entryIds: z10.array(z10.string().min(1)).min(1).describe("Memory entry ids that were actually used."),
|
|
1249
|
-
signal: z10.enum(["context_included", "cited", "explicit_positive"]).describe("Reinforcement signal: context_included < cited < explicit_positive."),
|
|
1250
|
-
at: z10.union([z10.string(), z10.number()]).optional().describe("Recall time as epoch-ms or full ISO-8601 timestamp with timezone."),
|
|
1251
|
-
...scopeShape
|
|
1252
|
-
};
|
|
1253
|
-
var reinforceTool = {
|
|
1254
|
-
name: "reinforce",
|
|
1255
|
-
config: {
|
|
1256
|
-
title: "Reinforce used pyx-memory entries",
|
|
1257
|
-
description: "Reinforce memories the agent actually USED (recall reinforces \u2014 raises strength so they surface in quick/medium effort tiers). signal: context_included < cited < explicit_positive.",
|
|
1258
|
-
inputSchema: inputShape6,
|
|
1259
|
-
annotations: { readOnlyHint: false, openWorldHint: true }
|
|
1260
|
-
},
|
|
1261
|
-
handler: (deps) => async (raw) => {
|
|
1262
|
-
const args = raw;
|
|
1263
|
-
const creds = await deps.readCredentials();
|
|
1264
|
-
if (!creds.ok) return creds.result;
|
|
1265
|
-
const http = createHttpClient(creds.credentials, deps.fetchImpl);
|
|
1266
|
-
const res = await http.requestJson({
|
|
1267
|
-
method: "POST",
|
|
1268
|
-
path: "/api/memory/reinforce",
|
|
1269
|
-
body: { entryIds: args.entryIds, signal: args.signal, at: args.at },
|
|
1270
|
-
scope: args
|
|
1271
|
-
});
|
|
1272
|
-
return res.ok ? mcpJson(res.data) : res.result;
|
|
1273
|
-
}
|
|
1274
|
-
};
|
|
1275
|
-
|
|
1276
|
-
// src/mcp/tools/search.ts
|
|
1277
|
-
import { z as z11 } from "zod";
|
|
1278
|
-
var inputShape7 = {
|
|
1279
|
-
query: z11.string().min(1).describe("Required natural-language search text."),
|
|
1280
|
-
limit: z11.number().int().min(1).max(100).optional().describe(SEARCH_LIMIT_DESC),
|
|
1281
|
-
strategy: z11.enum(["naive", "graph", "hybrid"]).optional().describe(
|
|
1282
|
-
"RAG strategy. Defaults to `hybrid` (cross-encoder reranking, multi-entity decomposition, confidence scoring) and is sent explicitly when omitted; pass `naive` for a lighter vector-only search or `graph` for graph-augmented retrieval."
|
|
1283
|
-
),
|
|
1284
|
-
effort: z11.enum(["quick", "medium", "deep"]).optional().describe(
|
|
1285
|
-
"Retrieval depth: quick=strongest, medium=default-depth, deep=everything including archived/superseded."
|
|
1286
|
-
),
|
|
1287
|
-
type: z11.enum(["short-term", "long-term", "working", "episodic", "summary"]).optional().describe("Filter by memory type."),
|
|
1288
|
-
agentId: z11.string().optional().describe("Filter to memories stored for this agentId."),
|
|
1289
|
-
abstentionThreshold: z11.number().min(0).max(1).optional().describe("Enable confidence scoring; abstain when confidence falls below this value (0\u20131)."),
|
|
1290
|
-
eventTimeStart: z11.string().optional().describe("Inclusive event-time start (ISO-8601); must be paired with eventTimeEnd."),
|
|
1291
|
-
eventTimeEnd: z11.string().optional().describe("Inclusive event-time end (ISO-8601); must be paired with eventTimeStart."),
|
|
1292
|
-
asOf: z11.string().optional().describe("Only include memories ingested before this ISO-8601 timestamp."),
|
|
1293
|
-
anchorTime: z11.string().optional().describe(
|
|
1294
|
-
'Soft recency ANCHOR (ISO-8601) \u2014 ranks results by proximity to this time instead of now; never excludes anything. When the question names a relative time ("two months ago", "last year", "3\uB144 \uC804"), resolve it against the current date yourself and pass the absolute timestamp here; prefer this over eventTimeStart/End unless a strict window is required, because hard filters drop last-known-before facts.'
|
|
1295
|
-
),
|
|
1296
|
-
enumerationConcept: z11.string().trim().min(1).optional().describe(SEARCH_ENUMERATION_CONCEPT_DESC),
|
|
1297
|
-
enableRerank: z11.boolean().optional().describe(
|
|
1298
|
-
"Opt into multilingual cross-encoder reranking (hybrid strategy only). Sharply improves Korean/cross-lingual ordering at higher latency; leave off for the fast default path."
|
|
1299
|
-
),
|
|
1300
|
-
...scopeShape
|
|
1301
|
-
};
|
|
1302
|
-
var searchMemoriesTool = {
|
|
1303
|
-
name: "search_memories",
|
|
1304
|
-
config: {
|
|
1305
|
-
title: "Search pyx-memory",
|
|
1306
|
-
description: "Search durable pyx-memory entries with hybrid/vector/keyword/graph strategy, tenant scope, sensitivity access, and optional confidence/abstention.",
|
|
1307
|
-
inputSchema: inputShape7,
|
|
1308
|
-
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
1309
|
-
},
|
|
1310
|
-
handler: (deps) => async (raw) => {
|
|
1311
|
-
const args = raw;
|
|
1312
|
-
const creds = await deps.readCredentials();
|
|
1313
|
-
if (!creds.ok) return creds.result;
|
|
1314
|
-
const http = createHttpClient(creds.credentials, deps.fetchImpl);
|
|
1315
|
-
const res = await http.requestJson({
|
|
1316
|
-
method: "GET",
|
|
1317
|
-
path: "/api/memory/search",
|
|
1318
|
-
query: {
|
|
1319
|
-
query: args.query,
|
|
1320
|
-
limit: args.limit,
|
|
1321
|
-
// Force `hybrid` unless the caller explicitly overrides. The server's
|
|
1322
|
-
// own default is hybrid too, but older tenant instances default to
|
|
1323
|
-
// `naive` (1–2 results vs hybrid's 15–20) — sending it explicitly makes
|
|
1324
|
-
// recall deterministic regardless of the instance version.
|
|
1325
|
-
strategy: args.strategy ?? "hybrid",
|
|
1326
|
-
effort: args.effort,
|
|
1327
|
-
type: args.type,
|
|
1328
|
-
agentId: args.agentId,
|
|
1329
|
-
abstentionThreshold: args.abstentionThreshold,
|
|
1330
|
-
eventTimeStart: args.eventTimeStart,
|
|
1331
|
-
eventTimeEnd: args.eventTimeEnd,
|
|
1332
|
-
asOf: args.asOf,
|
|
1333
|
-
anchorTime: args.anchorTime,
|
|
1334
|
-
enumerationConcept: args.enumerationConcept,
|
|
1335
|
-
enableRerank: args.enableRerank ? "true" : void 0
|
|
1336
|
-
},
|
|
1337
|
-
scope: args
|
|
1338
|
-
});
|
|
1339
|
-
return res.ok ? mcpJson(res.data) : res.result;
|
|
1340
|
-
}
|
|
1341
|
-
};
|
|
1342
|
-
|
|
1343
|
-
// src/mcp/tools/status.ts
|
|
1344
|
-
var inputShape8 = {};
|
|
1345
|
-
var statusTool = {
|
|
1346
|
-
name: "status",
|
|
1347
|
-
config: {
|
|
1348
|
-
title: "Get pyx-memory topology",
|
|
1349
|
-
description: "Fetch the running pyx-memory server topology from GET /status when available. Hosted gateways that expose /health but not /status return an explicit topologyUnavailable result with the health payload.",
|
|
1350
|
-
inputSchema: inputShape8,
|
|
1351
|
-
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
1352
|
-
},
|
|
1353
|
-
handler: (deps) => async () => {
|
|
1354
|
-
const creds = await deps.readCredentials();
|
|
1355
|
-
if (!creds.ok) return creds.result;
|
|
1356
|
-
const http = createHttpClient(creds.credentials, deps.fetchImpl);
|
|
1357
|
-
const res = await http.requestJson({ method: "GET", path: "/status" });
|
|
1358
|
-
if (res.ok) return mcpJson(res.data);
|
|
1359
|
-
if (res.status !== 404) return res.result;
|
|
1360
|
-
const health = await http.requestJson({ method: "GET", path: "/health" });
|
|
1361
|
-
if (!health.ok) return res.result;
|
|
1362
|
-
return mcpJson({
|
|
1363
|
-
topologyUnavailable: true,
|
|
1364
|
-
reason: "GET /status returned 404; this endpoint exposes /health but not topology status.",
|
|
1365
|
-
statusEndpoint: "/status",
|
|
1366
|
-
healthEndpoint: "/health",
|
|
1367
|
-
health: health.data
|
|
1368
|
-
});
|
|
1369
|
-
}
|
|
1370
|
-
};
|
|
1371
|
-
|
|
1372
|
-
// src/mcp/tools/store.ts
|
|
1373
|
-
import { z as z12 } from "zod";
|
|
1374
|
-
var entityTypes = ["PERSON", "ORGANIZATION", "CONCEPT", "TOOL", "LOCATION", "EVENT"];
|
|
1375
|
-
var preferredRelationshipTypes = [
|
|
1376
|
-
"USES",
|
|
1377
|
-
"OWNS",
|
|
1378
|
-
"DEPENDS_ON",
|
|
1379
|
-
"RELATED_TO",
|
|
1380
|
-
"CREATED_BY",
|
|
1381
|
-
"PART_OF",
|
|
1382
|
-
"IS_A",
|
|
1383
|
-
"WORKS_AT",
|
|
1384
|
-
"LOCATED_IN"
|
|
1385
|
-
];
|
|
1386
|
-
var RELATIONSHIP_TYPE_DESC = `Relationship type \u2014 freeform label; preferred: ${preferredRelationshipTypes.join(", ")}.`;
|
|
1387
|
-
var storeTargets = ["sqlite", "vector", "graph"];
|
|
1388
|
-
var entityShape = z12.object({
|
|
1389
|
-
name: z12.string().min(1).describe("Entity name as referenced in content."),
|
|
1390
|
-
type: z12.enum(entityTypes).describe("Entity type.")
|
|
1391
|
-
});
|
|
1392
|
-
var inputShape9 = {
|
|
1393
|
-
content: z12.string().min(1).describe("Concise factual statement to persist; decision, not deliberation."),
|
|
1394
|
-
topic: z12.string().min(1).describe("Required metadata.topic for retrieval grouping."),
|
|
1395
|
-
project: z12.string().min(1).describe("Required metadata.project namespace."),
|
|
1396
|
-
type: z12.enum(["short-term", "long-term", "working", "episodic", "summary"]).optional().describe("Memory type. Default long-term."),
|
|
1397
|
-
targets: z12.array(z12.enum(storeTargets)).optional().describe(
|
|
1398
|
-
"Storage targets. Include 'graph' when you provide entities/relationships or want zero graph write counts reported."
|
|
1399
|
-
),
|
|
1400
|
-
importance: z12.number().int().min(1).max(10).optional().describe("Importance 1\u201310."),
|
|
1401
|
-
eventTime: z12.string().optional().describe(STORE_EVENT_TIME_DESC),
|
|
1402
|
-
source: z12.string().optional().describe("Free-form origin (filename, URL, conversation id)."),
|
|
1403
|
-
agentId: z12.string().optional().describe("Agent identifier stored alongside the entry."),
|
|
1404
|
-
sessionId: z12.string().optional().describe("Session identifier for grouping."),
|
|
1405
|
-
parentId: z12.string().optional().describe("Parent memory entry id (hierarchical)."),
|
|
1406
|
-
entities: z12.array(entityShape).optional().describe(STORE_ENTITIES_DESC),
|
|
1407
|
-
relationships: z12.array(
|
|
1408
|
-
z12.object({
|
|
1409
|
-
source: z12.string().min(1).describe("Source entity name (must appear in entities array)."),
|
|
1410
|
-
target: z12.string().min(1).describe("Target entity name (must appear in entities array)."),
|
|
1411
|
-
type: z12.string().min(1).describe(RELATIONSHIP_TYPE_DESC)
|
|
1412
|
-
})
|
|
1413
|
-
).optional().describe(STORE_RELATIONSHIPS_DESC),
|
|
1414
|
-
triples: z12.array(
|
|
1415
|
-
z12.object({
|
|
1416
|
-
subject: entityShape.describe("Subject entity (source node)."),
|
|
1417
|
-
relation: z12.string().min(1).describe(RELATIONSHIP_TYPE_DESC),
|
|
1418
|
-
object: entityShape.describe("Object entity (target node).")
|
|
1419
|
-
})
|
|
1420
|
-
).optional().describe(STORE_TRIPLES_DESC),
|
|
1421
|
-
extractEntities: z12.boolean().optional().describe(
|
|
1422
|
-
"Override extraction: false skips extraction; true asks the server-side extraction brain to run and errors loudly when none is configured."
|
|
1423
|
-
),
|
|
1424
|
-
entitiesOnly: z12.boolean().optional().describe(
|
|
1425
|
-
"Set true to deliberately store entities with no relationships (e.g. a single concept). Otherwise a graph store with 2+ entities and 0 relationships is refused so isolated nodes do not accumulate."
|
|
1426
|
-
),
|
|
1427
|
-
...scopeShape
|
|
1428
|
-
};
|
|
1429
|
-
function materializeGraphInput(args) {
|
|
1430
|
-
const entities = /* @__PURE__ */ new Map();
|
|
1431
|
-
const addEntity = (e) => {
|
|
1432
|
-
const key = `${normalizeNameKey(e.name)}|${e.type}`;
|
|
1433
|
-
if (!entities.has(key)) entities.set(key, e);
|
|
1434
|
-
};
|
|
1435
|
-
const relationships = /* @__PURE__ */ new Map();
|
|
1436
|
-
const addRel = (rel) => {
|
|
1437
|
-
const key = `${normalizeNameKey(rel.source)}|${normalizeNameKey(rel.target)}|${normalizeGraphLabel(rel.type, "RELATED_TO")}`;
|
|
1438
|
-
if (!relationships.has(key)) relationships.set(key, rel);
|
|
1439
|
-
};
|
|
1440
|
-
for (const e of args.entities ?? []) addEntity(e);
|
|
1441
|
-
for (const r of args.relationships ?? []) addRel(r);
|
|
1442
|
-
for (const t of args.triples ?? []) {
|
|
1443
|
-
addEntity(t.subject);
|
|
1444
|
-
addEntity(t.object);
|
|
1445
|
-
addRel({ source: t.subject.name, target: t.object.name, type: t.relation });
|
|
1446
|
-
}
|
|
1447
|
-
return { entities: [...entities.values()], relationships: [...relationships.values()] };
|
|
1448
|
-
}
|
|
1449
|
-
var storeMemoryTool = {
|
|
1450
|
-
name: "store_memory",
|
|
1451
|
-
config: {
|
|
1452
|
-
title: "Store pyx-memory entry",
|
|
1453
|
-
description: STORE_TOOL_DESC,
|
|
1454
|
-
inputSchema: inputShape9,
|
|
1455
|
-
annotations: { readOnlyHint: false, idempotentHint: false, openWorldHint: true }
|
|
1456
|
-
},
|
|
1457
|
-
handler: (deps) => async (raw) => {
|
|
1458
|
-
const args = raw;
|
|
1459
|
-
const { entities, relationships } = materializeGraphInput(args);
|
|
1460
|
-
const graphTargeted = !args.targets || args.targets.includes("graph");
|
|
1461
|
-
const entityNameKeys = new Set(entities.map((e) => normalizeNameKey(e.name)));
|
|
1462
|
-
const resolvableRelationships = relationships.filter((r) => {
|
|
1463
|
-
const source = normalizeNameKey(r.source);
|
|
1464
|
-
const target = normalizeNameKey(r.target);
|
|
1465
|
-
return source !== target && entityNameKeys.has(source) && entityNameKeys.has(target);
|
|
1466
|
-
}).length;
|
|
1467
|
-
if (graphTargeted && entities.length >= 2 && resolvableRelationships === 0 && !args.entitiesOnly && args.extractEntities !== true) {
|
|
1468
|
-
return mcpText(
|
|
1469
|
-
`GRAPH_RELATIONSHIPS_REQUIRED: this store_memory call declares ${entities.length} entities but no relationship that connects them. Isolated nodes will not connect into the knowledge graph. Re-call store_memory with a \`relationships\` array (or \`triples\`) linking the entities (each { source, target, type }; source/target must be entity names from this call), OR set \`entitiesOnly: true\` if these entities are intentionally unconnected. Do not retry unchanged. If your MCP host exposes prompts, the \`structure_graph\` prompt produces the relationship/triple fields to add.`,
|
|
1470
|
-
true
|
|
1471
|
-
);
|
|
1472
|
-
}
|
|
1473
|
-
const creds = await deps.readCredentials();
|
|
1474
|
-
if (!creds.ok) return creds.result;
|
|
1475
|
-
const http = createHttpClient(creds.credentials, deps.fetchImpl);
|
|
1476
|
-
const body = {
|
|
1477
|
-
content: args.content,
|
|
1478
|
-
type: args.type ?? "long-term",
|
|
1479
|
-
targets: args.targets,
|
|
1480
|
-
metadata: {
|
|
1481
|
-
source: "agent",
|
|
1482
|
-
topic: args.topic,
|
|
1483
|
-
project: args.project
|
|
1484
|
-
},
|
|
1485
|
-
importance: args.importance,
|
|
1486
|
-
eventTime: args.eventTime,
|
|
1487
|
-
agentId: args.agentId,
|
|
1488
|
-
sessionId: args.sessionId,
|
|
1489
|
-
parentId: args.parentId,
|
|
1490
|
-
entities: entities.length > 0 ? entities : void 0,
|
|
1491
|
-
relationships: relationships.length > 0 ? relationships : void 0,
|
|
1492
|
-
...args.extractEntities !== void 0 ? { extractEntities: args.extractEntities } : {}
|
|
1493
|
-
};
|
|
1494
|
-
const res = await http.requestJson({
|
|
1495
|
-
method: "POST",
|
|
1496
|
-
path: "/api/memory/ingest",
|
|
1497
|
-
body,
|
|
1498
|
-
scope: args
|
|
1499
|
-
});
|
|
1500
|
-
return res.ok ? mcpJson(res.data) : res.result;
|
|
1501
|
-
}
|
|
1502
|
-
};
|
|
1503
|
-
|
|
1504
|
-
// src/mcp/tools/summarize.ts
|
|
1505
|
-
import { z as z13 } from "zod";
|
|
1506
|
-
var inputShape10 = {
|
|
1507
|
-
entityName: z13.string().min(1).describe(
|
|
1508
|
-
"Entity name to synthesize a profile for (PERSON, ORG, CONCEPT, TOOL, LOCATION, EVENT)."
|
|
1509
|
-
),
|
|
1510
|
-
entityType: z13.enum(["PERSON", "ORGANIZATION", "CONCEPT", "TOOL", "LOCATION", "EVENT"]).optional().describe("Optional explicit entity type when the same name spans multiple categories."),
|
|
1511
|
-
refresh: z13.boolean().optional().describe(
|
|
1512
|
-
"Default false: fetch the existing synthesis. true: rebuild the synthesis from current memory (POST)."
|
|
1513
|
-
),
|
|
1514
|
-
...scopeShape
|
|
1515
|
-
};
|
|
1516
|
-
var summarizeMemoryEntityTool = {
|
|
1517
|
-
name: "summarize_memory_entity",
|
|
1518
|
-
config: {
|
|
1519
|
-
title: "Synthesize pyx-memory entity",
|
|
1520
|
-
description: "Fetch or refresh a reusable synthesis/profile for an entity (person, customer, project, tool, organization). Default fetches existing; set refresh:true to rebuild.",
|
|
1521
|
-
inputSchema: inputShape10,
|
|
1522
|
-
annotations: { readOnlyHint: false, idempotentHint: true, openWorldHint: true }
|
|
1523
|
-
},
|
|
1524
|
-
handler: (deps) => async (raw) => {
|
|
1525
|
-
const args = raw;
|
|
1526
|
-
const creds = await deps.readCredentials();
|
|
1527
|
-
if (!creds.ok) return creds.result;
|
|
1528
|
-
const http = createHttpClient(creds.credentials, deps.fetchImpl);
|
|
1529
|
-
if (args.refresh) {
|
|
1530
|
-
const res2 = await http.requestJson({
|
|
1531
|
-
method: "POST",
|
|
1532
|
-
path: "/api/memory/synthesis/entity",
|
|
1533
|
-
body: { name: args.entityName, entityType: args.entityType },
|
|
1534
|
-
scope: args
|
|
1535
|
-
});
|
|
1536
|
-
return res2.ok ? mcpJson(res2.data) : res2.result;
|
|
1537
|
-
}
|
|
1538
|
-
const res = await http.requestJson({
|
|
1539
|
-
method: "GET",
|
|
1540
|
-
path: "/api/memory/synthesis/entity",
|
|
1541
|
-
query: { name: args.entityName, entityType: args.entityType },
|
|
1542
|
-
scope: args
|
|
1543
|
-
});
|
|
1544
|
-
return res.ok ? mcpJson(res.data) : res.result;
|
|
1545
|
-
}
|
|
1546
|
-
};
|
|
1547
|
-
|
|
1548
|
-
// src/mcp/tools/index.ts
|
|
1549
|
-
var ALL_TOOLS = [
|
|
1550
|
-
searchMemoriesTool,
|
|
1551
|
-
storeMemoryTool,
|
|
1552
|
-
getMemoryTool,
|
|
1553
|
-
lineageTool,
|
|
1554
|
-
reinforceTool,
|
|
1555
|
-
listMemoriesTool,
|
|
1556
|
-
deleteMemoryTool,
|
|
1557
|
-
ingestMemoryFileTool,
|
|
1558
|
-
summarizeMemoryEntityTool,
|
|
1559
|
-
statusTool,
|
|
1560
|
-
getUserProfileTool,
|
|
1561
|
-
upsertUserProfileTool,
|
|
1562
|
-
recordCorrectionTool,
|
|
1563
|
-
fetchApplicableCorrectionsTool
|
|
1564
|
-
];
|
|
1565
|
-
var ALL_TOOL_NAMES = ALL_TOOLS.map((t) => t.name);
|
|
1566
|
-
|
|
1567
|
-
// src/mcp/server.ts
|
|
1568
|
-
async function runMcpServer(opts) {
|
|
1569
|
-
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
1570
|
-
const version = opts.version ?? (true ? "1.2.0" : "0.0.0-dev");
|
|
1571
|
-
const server = new McpServer(
|
|
1572
|
-
{ name: "pyx-memory", version },
|
|
1573
|
-
{ instructions: PYX_MEMORY_INSTRUCTIONS, capabilities: { tools: {}, prompts: {} } }
|
|
1574
|
-
);
|
|
1575
|
-
for (const tool of ALL_TOOLS) {
|
|
1576
|
-
const handle = tool.handler({
|
|
1577
|
-
readCredentials: opts.readCredentials,
|
|
1578
|
-
fetchImpl
|
|
1579
|
-
});
|
|
1580
|
-
server.registerTool(
|
|
1581
|
-
tool.name,
|
|
1582
|
-
tool.config,
|
|
1583
|
-
async (args, extra) => handle(args, { signal: extra?.signal })
|
|
1584
|
-
);
|
|
1585
|
-
}
|
|
1586
|
-
for (const prompt of ALL_PROMPTS) {
|
|
1587
|
-
prompt.register(server);
|
|
1588
|
-
}
|
|
1589
|
-
const transport = new StdioServerTransport2();
|
|
1590
|
-
const closed = new Promise((resolve4) => {
|
|
1591
|
-
transport.onclose = () => resolve4();
|
|
1592
|
-
});
|
|
1593
|
-
await server.connect(transport);
|
|
1594
|
-
await closed;
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
834
|
// src/cli/commands/mcp.ts
|
|
1598
|
-
async function mcpCommand(
|
|
835
|
+
async function mcpCommand() {
|
|
1599
836
|
const readCredentials = createReadCredentials(() => getDefaultKeychain());
|
|
1600
|
-
const onStdinEnd = new Promise((
|
|
1601
|
-
process.stdin.once("end",
|
|
1602
|
-
process.stdin.once("close",
|
|
837
|
+
const onStdinEnd = new Promise((resolve3) => {
|
|
838
|
+
process.stdin.once("end", resolve3);
|
|
839
|
+
process.stdin.once("close", resolve3);
|
|
1603
840
|
});
|
|
1604
841
|
try {
|
|
1605
|
-
|
|
1606
|
-
await Promise.race([run, onStdinEnd]);
|
|
842
|
+
await Promise.race([runMcpProxyServer({ readCredentials }), onStdinEnd]);
|
|
1607
843
|
return EXIT.OK;
|
|
1608
844
|
} catch (err) {
|
|
1609
845
|
process.stderr.write(`Internal error: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -1612,12 +848,13 @@ async function mcpCommand(opts = {}) {
|
|
|
1612
848
|
}
|
|
1613
849
|
}
|
|
1614
850
|
function resolveMcpMode(flags) {
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
851
|
+
if (flags.bundled === true) {
|
|
852
|
+
return {
|
|
853
|
+
ok: false,
|
|
854
|
+
error: "The bundled stdio MCP server was removed in v1.3.0. `pyx-mem mcp` now uses the hosted zero-touch path (server-owned tools + instructions; your key stays in the OS keychain). Drop `--bundled`. For a self-hosted server, point at it with `pyx-mem login --endpoint <url>` first."
|
|
855
|
+
};
|
|
1619
856
|
}
|
|
1620
|
-
return { ok: true
|
|
857
|
+
return { ok: true };
|
|
1621
858
|
}
|
|
1622
859
|
|
|
1623
860
|
// src/cli/commands/mcp-install.ts
|
|
@@ -2075,7 +1312,7 @@ function writeJsonAndReport(filePath, agentLabel, entry, opts = {}) {
|
|
|
2075
1312
|
|
|
2076
1313
|
// src/cli/commands/scaffold.ts
|
|
2077
1314
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
|
|
2078
|
-
import { basename as
|
|
1315
|
+
import { basename as basename2, join as join2, resolve as resolve2 } from "path";
|
|
2079
1316
|
var SERVER_IMAGE = "ghcr.io/pyx-corp/pyx-memory-v1:latest";
|
|
2080
1317
|
var DOCKER_COMPOSE = `services:
|
|
2081
1318
|
pyx-memory:
|
|
@@ -2198,8 +1435,8 @@ function resolveTarget(args) {
|
|
|
2198
1435
|
process.stderr.write("Error: --name requires a non-empty directory name.\n");
|
|
2199
1436
|
return null;
|
|
2200
1437
|
}
|
|
2201
|
-
const targetDir = trimmedName ?
|
|
2202
|
-
const appName = trimmedName ||
|
|
1438
|
+
const targetDir = trimmedName ? resolve2(cwd, trimmedName) : cwd;
|
|
1439
|
+
const appName = trimmedName || basename2(resolve2(cwd));
|
|
2203
1440
|
return { targetDir, appName };
|
|
2204
1441
|
}
|
|
2205
1442
|
function buildFiles(targetDir, appName) {
|
|
@@ -2225,7 +1462,7 @@ function scaffoldCommand(args = {}) {
|
|
|
2225
1462
|
const created = [];
|
|
2226
1463
|
const skipped = [];
|
|
2227
1464
|
for (const file of buildFiles(target.targetDir, target.appName)) {
|
|
2228
|
-
const relativePath =
|
|
1465
|
+
const relativePath = basename2(file.path);
|
|
2229
1466
|
if (existsSync2(file.path)) {
|
|
2230
1467
|
skipped.push(relativePath);
|
|
2231
1468
|
continue;
|
|
@@ -2305,21 +1542,22 @@ Commands:
|
|
|
2305
1542
|
logout Delete stored pyx-memory credentials.
|
|
2306
1543
|
doctor [--json] Diagnose keychain, credentials, backend, MCP startup.
|
|
2307
1544
|
scaffold [--name <dir>] Generate Docker, env, SDK, and memory design-guide starter files.
|
|
2308
|
-
mcp
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
1545
|
+
mcp Start the stdio MCP proxy to the hosted server, so
|
|
1546
|
+
tools + instructions stay server-owned (zero-touch
|
|
1547
|
+
updates; key stays in the OS keychain). Self-host:
|
|
1548
|
+
run 'login --endpoint <url>' first to point it at
|
|
1549
|
+
your own server.
|
|
2313
1550
|
mcp install <target> [--scope user|local|project] [--remote]
|
|
2314
|
-
Install pyx-memory MCP config for your AI agent.
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
1551
|
+
Install pyx-memory MCP config for your AI agent. The
|
|
1552
|
+
config launches the hosted zero-touch thin proxy;
|
|
1553
|
+
--remote is an accepted no-op alias (the bundled
|
|
1554
|
+
stdio server was removed in v1.3.0).
|
|
2318
1555
|
Targets: claude-code, codex, cursor, cline, continue, windsurf, gemini-cli, pi, oh-my-pi, openclaw, hermes.
|
|
2319
1556
|
|
|
2320
1557
|
Notes:
|
|
2321
1558
|
- Credentials are stored only in the OS credential store (Keychain / libsecret / Credential Manager).
|
|
2322
1559
|
- No plaintext token file is ever written.
|
|
1560
|
+
- \`mcp --bundled\` was removed in v1.3.0 and now errors \u2014 drop the flag (the hosted path is the only transport).
|
|
2323
1561
|
- Run \`pyx-mem doctor\` if something looks wrong.
|
|
2324
1562
|
`;
|
|
2325
1563
|
|
|
@@ -2404,12 +1642,7 @@ function runMcpCommand(parsed) {
|
|
|
2404
1642
|
`);
|
|
2405
1643
|
return EXIT.USAGE;
|
|
2406
1644
|
}
|
|
2407
|
-
|
|
2408
|
-
process.stderr.write(
|
|
2409
|
-
"pyx-memory: `pyx-mem mcp` now connects to the hosted server (zero-touch \u2014 tools and instructions stay server-owned). Pass `--bundled` for the legacy in-package server.\n"
|
|
2410
|
-
);
|
|
2411
|
-
}
|
|
2412
|
-
return mcpCommand({ bundled: resolved.bundled });
|
|
1645
|
+
return mcpCommand();
|
|
2413
1646
|
}
|
|
2414
1647
|
if (parsed.subcommand === "install") {
|
|
2415
1648
|
const target = parsed.positional[0];
|
package/dist/dashboard.mjs
CHANGED
|
@@ -11,9 +11,8 @@ import {
|
|
|
11
11
|
toGraphologyFormat,
|
|
12
12
|
transformGraphData,
|
|
13
13
|
unreachableHealth
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-X6AYWXW7.mjs";
|
|
14
|
+
} from "./chunk-MZF55IUR.mjs";
|
|
15
|
+
import "./chunk-BS6K64SA.mjs";
|
|
17
16
|
export {
|
|
18
17
|
DashboardClient,
|
|
19
18
|
Poller,
|
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DisabledMemory,
|
|
3
|
-
MemoryClient,
|
|
4
|
-
MemoryServerError
|
|
5
|
-
} from "./chunk-3SDKJ5TB.mjs";
|
|
6
1
|
import {
|
|
7
2
|
DEFAULTS,
|
|
8
3
|
DEPRECATED_RAG_STRATEGIES,
|
|
4
|
+
DisabledMemory,
|
|
9
5
|
EmbeddingProviderName,
|
|
6
|
+
MemoryClient,
|
|
7
|
+
MemoryServerError,
|
|
10
8
|
MemoryType,
|
|
11
9
|
MoveFailureReason,
|
|
12
10
|
NamespaceIsolation,
|
|
@@ -18,7 +16,7 @@ import {
|
|
|
18
16
|
mergeExtractedEntities,
|
|
19
17
|
normalizeGraphLabel,
|
|
20
18
|
normalizeNameKey
|
|
21
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-BS6K64SA.mjs";
|
|
22
20
|
|
|
23
21
|
// src/preset.ts
|
|
24
22
|
var DEFAULT_MEMORY_URL = `http://localhost:${DEFAULTS.MEMORY_SERVER_PORT}`;
|
package/dist/react.mjs
CHANGED
|
@@ -11,9 +11,8 @@ import {
|
|
|
11
11
|
toGraphologyFormat,
|
|
12
12
|
transformGraphData,
|
|
13
13
|
unreachableHealth
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-X6AYWXW7.mjs";
|
|
14
|
+
} from "./chunk-MZF55IUR.mjs";
|
|
15
|
+
import "./chunk-BS6K64SA.mjs";
|
|
17
16
|
|
|
18
17
|
// ../dashboard/src/hooks/use-consolidation-log.ts
|
|
19
18
|
import { useCallback as useCallback2, useMemo } from "react";
|
package/package.json
CHANGED
package/dist/chunk-X6AYWXW7.mjs
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
// ../shared/src/constants/defaults.ts
|
|
2
|
-
var DEFAULTS = {
|
|
3
|
-
DATA_DIR: "./data",
|
|
4
|
-
VECTOR_PROVIDER: "lancedb",
|
|
5
|
-
MEMORY_SERVER_PORT: 7822
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
// ../shared/src/graph/extraction.ts
|
|
9
|
-
function normalizeGraphLabel(value, fallback) {
|
|
10
|
-
const normalized = value.trim().toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
11
|
-
return normalized.length > 0 ? normalized : fallback;
|
|
12
|
-
}
|
|
13
|
-
function normalizeNameKey(name) {
|
|
14
|
-
return name.trim().toLowerCase().replace(/\s+/g, " ");
|
|
15
|
-
}
|
|
16
|
-
function relationshipKey(relationship) {
|
|
17
|
-
return [
|
|
18
|
-
relationship.source.trim().toLowerCase(),
|
|
19
|
-
relationship.target.trim().toLowerCase(),
|
|
20
|
-
normalizeGraphLabel(relationship.type, "RELATED_TO")
|
|
21
|
-
].join("\0");
|
|
22
|
-
}
|
|
23
|
-
function mergeExtractedEntities(callerEntities, callerRelationships, extracted) {
|
|
24
|
-
const entities = [...callerEntities ?? []];
|
|
25
|
-
const relationships = [...callerRelationships ?? []];
|
|
26
|
-
const nameByLowercase = /* @__PURE__ */ new Map();
|
|
27
|
-
for (const entity of entities) {
|
|
28
|
-
const key = entity.name.toLowerCase();
|
|
29
|
-
if (!nameByLowercase.has(key)) nameByLowercase.set(key, entity.name);
|
|
30
|
-
}
|
|
31
|
-
for (const entity of extracted.entities) {
|
|
32
|
-
const key = entity.name.toLowerCase();
|
|
33
|
-
if (nameByLowercase.has(key)) continue;
|
|
34
|
-
entities.push({ ...entity, type: normalizeGraphLabel(entity.type, "CONCEPT") });
|
|
35
|
-
nameByLowercase.set(key, entity.name);
|
|
36
|
-
}
|
|
37
|
-
for (const relationship of extracted.relations) {
|
|
38
|
-
const source = nameByLowercase.get(relationship.source.toLowerCase());
|
|
39
|
-
const target = nameByLowercase.get(relationship.target.toLowerCase());
|
|
40
|
-
if (source && target) {
|
|
41
|
-
relationships.push({
|
|
42
|
-
...relationship,
|
|
43
|
-
source,
|
|
44
|
-
target,
|
|
45
|
-
type: normalizeGraphLabel(relationship.type, "RELATED_TO")
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
const seenRelationships = /* @__PURE__ */ new Set();
|
|
50
|
-
const dedupedRelationships = [];
|
|
51
|
-
for (const relationship of relationships) {
|
|
52
|
-
const key = relationshipKey(relationship);
|
|
53
|
-
if (seenRelationships.has(key)) continue;
|
|
54
|
-
seenRelationships.add(key);
|
|
55
|
-
dedupedRelationships.push(relationship);
|
|
56
|
-
}
|
|
57
|
-
return { entities, relationships: dedupedRelationships };
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ../shared/src/types/isolation.ts
|
|
61
|
-
var NamespaceIsolation = {
|
|
62
|
-
SHARED: "shared",
|
|
63
|
-
STRICT: "strict"
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
// ../shared/src/types/memory.ts
|
|
67
|
-
var MemoryType = {
|
|
68
|
-
SHORT_TERM: "short-term",
|
|
69
|
-
LONG_TERM: "long-term",
|
|
70
|
-
WORKING: "working",
|
|
71
|
-
EPISODIC: "episodic",
|
|
72
|
-
SUMMARY: "summary"
|
|
73
|
-
};
|
|
74
|
-
var SensitivityLevel = {
|
|
75
|
-
PUBLIC: "public",
|
|
76
|
-
INTERNAL: "internal",
|
|
77
|
-
SECRET: "secret"
|
|
78
|
-
};
|
|
79
|
-
var RAGStrategy = {
|
|
80
|
-
NAIVE: "naive",
|
|
81
|
-
GRAPH: "graph",
|
|
82
|
-
HYBRID: "hybrid"
|
|
83
|
-
};
|
|
84
|
-
var DEPRECATED_RAG_STRATEGIES = /* @__PURE__ */ new Map([
|
|
85
|
-
["agentic", "strategy.deprecated:agentic \u2014 removed in v0.26, use hybrid"]
|
|
86
|
-
]);
|
|
87
|
-
var VectorProvider = {
|
|
88
|
-
LANCEDB: "lancedb"
|
|
89
|
-
};
|
|
90
|
-
var EmbeddingProviderName = {
|
|
91
|
-
STUB: "stub",
|
|
92
|
-
/** @deprecated Vestigial — pyx-memory uses internal EmbeddingGemma embeddings. */
|
|
93
|
-
ANTHROPIC: "anthropic",
|
|
94
|
-
/** @deprecated Vestigial — pyx-memory uses internal EmbeddingGemma embeddings. */
|
|
95
|
-
OPENAI: "openai",
|
|
96
|
-
/** In-process ONNX model (default: EmbeddingGemma-300M). */
|
|
97
|
-
LOCAL: "local",
|
|
98
|
-
/** Remote OpenAI-compatible embedding service (pyx-cloud shared, custom, etc.). */
|
|
99
|
-
HTTP: "http"
|
|
100
|
-
};
|
|
101
|
-
var StoreTarget = {
|
|
102
|
-
SQLITE: "sqlite",
|
|
103
|
-
VECTOR: "vector",
|
|
104
|
-
GRAPH: "graph"
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
// ../shared/src/types/move.ts
|
|
108
|
-
var MoveFailureReason = {
|
|
109
|
-
/** Entry not found in the caller's tenant. */
|
|
110
|
-
NOT_FOUND: "not_found",
|
|
111
|
-
/** Move would cross tenant boundary (always forbidden). */
|
|
112
|
-
CROSS_TENANT_FORBIDDEN: "cross_tenant_forbidden",
|
|
113
|
-
/** Target namespace ID does not exist in the caller's tenant. */
|
|
114
|
-
TARGET_NAMESPACE_NOT_FOUND: "target_namespace_not_found",
|
|
115
|
-
/** SQLite metadata update failed; no compensation needed. */
|
|
116
|
-
SQLITE_UPDATE_FAILED: "sqlite_update_failed",
|
|
117
|
-
/** Vector store metadata update failed; SQLite reverted. */
|
|
118
|
-
VECTOR_UPDATE_FAILED: "vector_update_failed",
|
|
119
|
-
/** Graph edge namespace update failed; SQLite + vector reverted. */
|
|
120
|
-
GRAPH_UPDATE_FAILED: "graph_update_failed",
|
|
121
|
-
/** Compensation itself failed — manual intervention required. */
|
|
122
|
-
COMPENSATION_FAILED: "compensation_failed"
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// ../shared/src/types/principal.ts
|
|
126
|
-
var SINGLE_TENANT_ID = "_single";
|
|
127
|
-
|
|
128
|
-
export {
|
|
129
|
-
DEFAULTS,
|
|
130
|
-
normalizeGraphLabel,
|
|
131
|
-
normalizeNameKey,
|
|
132
|
-
mergeExtractedEntities,
|
|
133
|
-
NamespaceIsolation,
|
|
134
|
-
MemoryType,
|
|
135
|
-
SensitivityLevel,
|
|
136
|
-
RAGStrategy,
|
|
137
|
-
DEPRECATED_RAG_STRATEGIES,
|
|
138
|
-
VectorProvider,
|
|
139
|
-
EmbeddingProviderName,
|
|
140
|
-
StoreTarget,
|
|
141
|
-
MoveFailureReason,
|
|
142
|
-
SINGLE_TENANT_ID
|
|
143
|
-
};
|