@pyxmate/memory 1.2.0 → 1.4.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.
@@ -1,4 +1,4 @@
1
- declare const PYX_MEMORY_INSTRUCTIONS = "Use pyx-memory to search durable project/user memory before assuming prior decisions, and to store concise facts after corrections, bug fixes, design decisions, integration discoveries, gotchas, explicit preferences, or \"remember this\" requests. Store decisions, not deliberation. Include topic and project. Pass eventTime (ISO-8601, when the fact happened or took effect) for any fact that can change or go stale \u2014 job/status changes, decisions that supersede earlier ones, dated events; recency ordering, dated (\"as of\") queries, and stale-vs-current conflict resolution all key off it. Search effort is retrieval depth: quick=strongest, deep=everything including archived/superseded. Use reinforce after memories were actually used, so they surface in quick/medium effort tiers. Use lineage for the history of how a fact changed; pass subject+relation for graph lineage or entryId for a superseded chain. Use record_correction when the user corrects a mistake you made (what was wrong, what to do instead, when it applies); call fetch_applicable_corrections before a task to retrieve the corrections that match it, then decide which to follow \u2014 pyx never auto-applies them. Isolation: userId/teamId are attribution and legacy filters, not authorization or read gates. Use agentId only for trusted per-agent pool isolation (hard filter when set; omit to share a pool); on hosted MaaS/remote MCP it is a within-tenant filter, not a cross-identity security boundary. For per-user/team privacy inside a shared tenant, use namespaces + ReBAC grants via /api/admin/* + ADMIN_API_KEY + TENANT_MODE=multi. callerAccessLevel is sensitivity redaction, not isolation. Trusted stdio/self-host/companion callers may pass scope per call; hosted MaaS/remote MCP derive identity from project/token and strip caller-supplied identity until verified-JWT multi-tenant remote MCP exists. When a question names a time \u2014 explicit or relative (\"last year\", \"\uB450 \uB2EC \uC804\") \u2014 resolve it to an absolute ISO-8601 timestamp yourself and pass it as search anchorTime: results then rank by proximity to this time instead of now, without excluding anything. For count/list questions about a category, pass `enumerationConcept` = the language-agnostic/global category phrase (e.g. \"fitness classes\" / \"\uC6B4\uB3D9 \uC218\uC5C5\"); the caller-supplied hint is resolved by the embedding model, which covers a broad range of languages. pyx also auto-detects English/Korean count phrasing as a best-effort fallback when the hint is omitted. When content names people, organizations, tools, places, events, or key concepts, pass entities and relationships when you can (caller-wins). You build the graph \u2014 the server does not extract it for you unless a self-host operator has configured a BYO extraction endpoint (then caller-passed graph still wins). On a multi-entity store, include at least one relationship connecting the entities, or the store is refused so isolated nodes never accumulate (set entitiesOnly for a deliberate lone entity); triples make edges impossible to omit. Edges matter as much as nodes: without relationships the graph cannot connect related memories. For countable categories the user may later enumerate (\"how many fitness classes / streaming services / pets do I have?\"), add a canonical category CONCEPT node and an IS_A edge from each member to it. A language/synonym variant gives zero-cost exact-match resolution only when it is also a memberful CONCEPT node with member IS_A edges; there is no query-time alias\u2192canonical resolver. pyx resolves count/list category hints at query time via strict embedding over memberful CONCEPT nodes. Use file ingest for documents/images worth persisting; images require a description.";
1
+ declare const PYX_MEMORY_INSTRUCTIONS = "Use pyx-memory for durable memory across sessions, proactively \u2014 do not wait to be told. SEARCH before assuming: at the start of a task, and before proposing an approach or re-deriving a past decision, search memory for the project and topic; resolve relative times (\"last year\", \"\uB450 \uB2EC \uC804\") to an absolute ISO-8601 anchorTime so results rank by proximity to that time. STORE once a fact is settled: corrections, bug fixes (root cause + fix), design/architecture decisions (with reasoning), integration/API/auth/endpoint details, gotchas, explicit preferences, and \"remember this\" requests \u2014 concise facts not deliberation, each with topic and project. Pass eventTime (ISO-8601, when the fact took effect) for anything that can change or go stale; recency ordering, \"as of\" queries, and stale-vs-current resolution all key off it. After a memory informs your work, call reinforce so it stays in the quick/medium tiers (idle never revives it). When the user corrects you, call record_correction; before a task, call fetch_applicable_corrections (pyx never auto-applies them). When content names people, organizations, tools, places, events, or key concepts, pass entities and relationships \u2014 you build the graph, the server does not extract it; a multi-entity store with no connecting edge is refused. Match search effort to need: quick (default, strongest) for routine recall, deep for full/archived history; use lineage to trace how a fact changed. userId/teamId/agentId and callerAccessLevel are attribution filters and sensitivity redaction, not a security isolation boundary \u2014 for tenant/namespace/ReBAC detail see the installed Persistent Memory rule.";
2
2
  declare const PERSISTENT_MEMORY_SECTION: string;
3
3
  declare function buildDesignGuide({ appName }: {
4
4
  appName: string;
@@ -13,7 +13,7 @@ import {
13
13
  buildAgentSnippet,
14
14
  buildDesignGuide,
15
15
  buildGraphStructuringPrompt
16
- } from "./chunk-XHEVB23R.mjs";
16
+ } from "./chunk-ML6VKYAL.mjs";
17
17
  export {
18
18
  AGENT_TARGETS,
19
19
  PERSISTENT_MEMORY_SECTION,
@@ -1,7 +1,129 @@
1
- import {
2
- RAGStrategy,
3
- mergeExtractedEntities
4
- } from "./chunk-X6AYWXW7.mjs";
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
@@ -1,5 +1,5 @@
1
1
  // src/contract/index.ts
2
- var PYX_MEMORY_INSTRUCTIONS = `Use pyx-memory to search durable project/user memory before assuming prior decisions, and to store concise facts after corrections, bug fixes, design decisions, integration discoveries, gotchas, explicit preferences, or "remember this" requests. Store decisions, not deliberation. Include topic and project. Pass eventTime (ISO-8601, when the fact happened or took effect) for any fact that can change or go stale \u2014 job/status changes, decisions that supersede earlier ones, dated events; recency ordering, dated ("as of") queries, and stale-vs-current conflict resolution all key off it. Search effort is retrieval depth: quick=strongest, deep=everything including archived/superseded. Use reinforce after memories were actually used, so they surface in quick/medium effort tiers. Use lineage for the history of how a fact changed; pass subject+relation for graph lineage or entryId for a superseded chain. Use record_correction when the user corrects a mistake you made (what was wrong, what to do instead, when it applies); call fetch_applicable_corrections before a task to retrieve the corrections that match it, then decide which to follow \u2014 pyx never auto-applies them. Isolation: userId/teamId are attribution and legacy filters, not authorization or read gates. Use agentId only for trusted per-agent pool isolation (hard filter when set; omit to share a pool); on hosted MaaS/remote MCP it is a within-tenant filter, not a cross-identity security boundary. For per-user/team privacy inside a shared tenant, use namespaces + ReBAC grants via /api/admin/* + ADMIN_API_KEY + TENANT_MODE=multi. callerAccessLevel is sensitivity redaction, not isolation. Trusted stdio/self-host/companion callers may pass scope per call; hosted MaaS/remote MCP derive identity from project/token and strip caller-supplied identity until verified-JWT multi-tenant remote MCP exists. When a question names a time \u2014 explicit or relative ("last year", "\uB450 \uB2EC \uC804") \u2014 resolve it to an absolute ISO-8601 timestamp yourself and pass it as search anchorTime: results then rank by proximity to this time instead of now, without excluding anything. For count/list questions about a category, pass \`enumerationConcept\` = the language-agnostic/global category phrase (e.g. "fitness classes" / "\uC6B4\uB3D9 \uC218\uC5C5"); the caller-supplied hint is resolved by the embedding model, which covers a broad range of languages. pyx also auto-detects English/Korean count phrasing as a best-effort fallback when the hint is omitted. When content names people, organizations, tools, places, events, or key concepts, pass entities and relationships when you can (caller-wins). You build the graph \u2014 the server does not extract it for you unless a self-host operator has configured a BYO extraction endpoint (then caller-passed graph still wins). On a multi-entity store, include at least one relationship connecting the entities, or the store is refused so isolated nodes never accumulate (set entitiesOnly for a deliberate lone entity); triples make edges impossible to omit. Edges matter as much as nodes: without relationships the graph cannot connect related memories. For countable categories the user may later enumerate ("how many fitness classes / streaming services / pets do I have?"), add a canonical category CONCEPT node and an IS_A edge from each member to it. A language/synonym variant gives zero-cost exact-match resolution only when it is also a memberful CONCEPT node with member IS_A edges; there is no query-time alias\u2192canonical resolver. pyx resolves count/list category hints at query time via strict embedding over memberful CONCEPT nodes. Use file ingest for documents/images worth persisting; images require a description.`;
2
+ var PYX_MEMORY_INSTRUCTIONS = `Use pyx-memory for durable memory across sessions, proactively \u2014 do not wait to be told. SEARCH before assuming: at the start of a task, and before proposing an approach or re-deriving a past decision, search memory for the project and topic; resolve relative times ("last year", "\uB450 \uB2EC \uC804") to an absolute ISO-8601 anchorTime so results rank by proximity to that time. STORE once a fact is settled: corrections, bug fixes (root cause + fix), design/architecture decisions (with reasoning), integration/API/auth/endpoint details, gotchas, explicit preferences, and "remember this" requests \u2014 concise facts not deliberation, each with topic and project. Pass eventTime (ISO-8601, when the fact took effect) for anything that can change or go stale; recency ordering, "as of" queries, and stale-vs-current resolution all key off it. After a memory informs your work, call reinforce so it stays in the quick/medium tiers (idle never revives it). When the user corrects you, call record_correction; before a task, call fetch_applicable_corrections (pyx never auto-applies them). When content names people, organizations, tools, places, events, or key concepts, pass entities and relationships \u2014 you build the graph, the server does not extract it; a multi-entity store with no connecting edge is refused. Match search effort to need: quick (default, strongest) for routine recall, deep for full/archived history; use lineage to trace how a fact changed. userId/teamId/agentId and callerAccessLevel are attribution filters and sensitivity redaction, not a security isolation boundary \u2014 for tenant/namespace/ReBAC detail see the installed Persistent Memory rule.`;
3
3
  var PERSISTENT_MEMORY_SECTION = [
4
4
  "## Persistent Memory",
5
5
  "",
@@ -142,7 +142,8 @@ function buildAgentSnippet() {
142
142
  ...targetLines,
143
143
  "",
144
144
  "Conflict policy:",
145
- "- If an existing `Persistent Memory` section references the `pyx-memory` MCP tools, REPLACE it with the version below \u2014 this is the newer canonical rule set for the same system, not a duplicate.",
145
+ "- If a pyx-memory managed block (between `<!-- pyx-memory:begin \u2026 -->` and `<!-- pyx-memory:end -->`) is already present \u2014 `pyx-mem mcp install` may have written it \u2014 leave it exactly as-is and do NOT add a second copy.",
146
+ "- Else if an existing `Persistent Memory` section references the `pyx-memory` MCP tools, REPLACE it with the version below \u2014 this is the newer canonical rule set for the same system, not a duplicate.",
146
147
  "- If an existing `Persistent Memory` section uses a different memory tool, append this one underneath instead. Do not delete, rewrite, or summarize the other tool's instructions.",
147
148
  "",
148
149
  "Install only the section below, verbatim. After writing, tell the user which file you updated.",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  MemoryClient
3
- } from "./chunk-3SDKJ5TB.mjs";
3
+ } from "./chunk-BS6K64SA.mjs";
4
4
 
5
5
  // ../dashboard/src/aggregations/consolidation-analytics.ts
6
6
  function analyzeConsolidationLog(entries) {