@pyxmate/memory 0.45.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -10
- package/dist/agent-contract.d.ts +40 -0
- package/dist/agent-contract.mjs +32 -0
- package/dist/{chunk-UV2DFSKR.mjs → chunk-PXQLVQAA.mjs} +1 -7
- package/dist/{chunk-KSTI4M52.mjs → chunk-X6AYWXW7.mjs} +20 -1
- package/dist/chunk-XHEVB23R.mjs +169 -0
- package/dist/{chunk-DKNGLNN4.mjs → chunk-ZCGJGI2O.mjs} +1 -1
- package/dist/cli/pyx-mem.mjs +358 -228
- package/dist/dashboard.mjs +3 -3
- package/dist/index.d.ts +87 -17
- package/dist/index.mjs +21 -4
- package/dist/react.mjs +3 -3
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -38,14 +38,13 @@ Restart Claude Code. The MCP tools are auto-discovered via MCP Tool Search:
|
|
|
38
38
|
`get_user_profile`, `upsert_user_profile`, `record_correction`, and
|
|
39
39
|
`fetch_applicable_corrections`.
|
|
40
40
|
|
|
41
|
-
**That's the whole setup
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
for the full contract.
|
|
41
|
+
**That's the whole setup.** Graph is a relational retrieval dimension, not a
|
|
42
|
+
search-score boost. When content names people, organizations, tools, places,
|
|
43
|
+
events, or key concepts, pass both `entities` and `relationships` when you can
|
|
44
|
+
(caller-wins); edges matter as much as nodes because traversal needs them to
|
|
45
|
+
connect related memories. Server-side text/entity extraction runs only when an
|
|
46
|
+
extraction brain is configured; otherwise callers must pass graph data or use
|
|
47
|
+
caller-side hooks. Images always require caller-provided descriptions/hooks.
|
|
49
48
|
|
|
50
49
|
Drop the [agent-template snippet](https://github.com/pyx-corp/pyx-memory-v1/blob/main/docs/agent-template.md)
|
|
51
50
|
into your project's `CLAUDE.md` / `AGENTS.md` to tell the agent WHEN to search
|
|
@@ -99,8 +98,8 @@ await memory.store(
|
|
|
99
98
|
Entity-free memories are valid. Store responses include `graphEntitiesWritten`
|
|
100
99
|
and `graphRelationshipsWritten`, with zero making an omitted graph write visible.
|
|
101
100
|
Per-call `entry.extractEntities: false` skips the callback entirely;
|
|
102
|
-
`entry.extractEntities: true` with no callback
|
|
103
|
-
|
|
101
|
+
`entry.extractEntities: true` with no callback forwards the hint to the server
|
|
102
|
+
brain, which loud-fails if it is not configured.
|
|
104
103
|
|
|
105
104
|
## Entry Points
|
|
106
105
|
|
|
@@ -0,0 +1,40 @@
|
|
|
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.";
|
|
2
|
+
declare const PERSISTENT_MEMORY_SECTION: string;
|
|
3
|
+
declare function buildDesignGuide({ appName }: {
|
|
4
|
+
appName: string;
|
|
5
|
+
}): string;
|
|
6
|
+
declare const AGENT_TARGETS: readonly [{
|
|
7
|
+
readonly tool: "Claude Code";
|
|
8
|
+
readonly instructionsFile: "~/.claude/CLAUDE.md";
|
|
9
|
+
}, {
|
|
10
|
+
readonly tool: "Codex CLI";
|
|
11
|
+
readonly instructionsFile: "~/.codex/AGENTS.md";
|
|
12
|
+
}, {
|
|
13
|
+
readonly tool: "oh-my-pi (omp)";
|
|
14
|
+
readonly instructionsFile: "~/.omp/agent/RULES.md";
|
|
15
|
+
}, {
|
|
16
|
+
readonly tool: "Gemini CLI";
|
|
17
|
+
readonly instructionsFile: "~/.gemini/GEMINI.md";
|
|
18
|
+
}, {
|
|
19
|
+
readonly tool: "Any other tool";
|
|
20
|
+
readonly instructionsFile: "that tool's user-scope (global) instructions/rules file";
|
|
21
|
+
}];
|
|
22
|
+
declare const STORE_ENTITIES_DESC = "Named entities mentioned by the content. 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.";
|
|
23
|
+
declare const STORE_RELATIONSHIPS_DESC = "Edges between entities; source and target must be entity names from this request. 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. Relationships matter as much as entities because graph traversal needs edges to 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 un-enumerated count-noun variants at query time via strict embedding over memberful CONCEPT nodes.";
|
|
24
|
+
declare const STORE_TRIPLES_DESC = "Graph facts as subject\u2013relation\u2013object triples; each is materialized into the two entity nodes plus the edge that connects them, so a node cannot be added without its relationship. Use this (or entities + relationships) to populate the knowledge graph \u2014 for weak hosts triples make edges unavoidable. subject/object each have a name and a type (from the entity-type set); relation is a freeform label. Merged with any entities/relationships you also pass (caller-wins); duplicates within one call are collapsed.";
|
|
25
|
+
declare const STORE_EVENT_TIME_DESC = "ISO-8601 time the fact happened or took effect (bi-temporal). Pass for any fact that can change or go stale \u2014 recency ordering, dated (\"as of\") queries, and stale-vs-current conflict resolution key off it.";
|
|
26
|
+
declare const STORE_TOOL_DESC = "Store one concise factual memory with required topic and project metadata. 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. Relationships (edges) matter as much as entities because graph traversal needs edges to connect related memories. Entity-free memories are valid; omit graph data or set extractEntities:false when there are no graph facts.";
|
|
27
|
+
declare const SEARCH_LIMIT_DESC = "Target result count; server clamps to 1\u2013100. Default 10. The hybrid strategy may widen count/list searches up to 30 for set-completeness recall when the caller passes `enumerationConcept`; English/Korean marker auto-detect is a best-effort fallback.";
|
|
28
|
+
declare const SEARCH_ENUMERATION_CONCEPT_DESC = "For count/list questions about a category, pass the language-agnostic/global category phrase here (for example, \"fitness classes\" or \"\uC6B4\uB3D9 \uC218\uC5C5\"). The embedding model resolves the caller-supplied hint across a broad range of languages.";
|
|
29
|
+
declare const STRUCTURE_GRAPH_PROMPT_DESC = "Returns a prompt that extracts the graph fields (triples, or entities + relationships) to include in a store_memory call \u2014 run it with your own LLM, then add the JSON it returns to your store_memory arguments (you still supply content/topic/project). Useful when store_memory returns GRAPH_RELATIONSHIPS_REQUIRED, or for any content with graph-worthy facts. This prompt is executed by your agent, not the server.";
|
|
30
|
+
/**
|
|
31
|
+
* Server-owned template the agent runs with its OWN LLM to extract the GRAPH
|
|
32
|
+
* FIELDS (not the whole store_memory call — the agent supplies content/topic/
|
|
33
|
+
* project) from `content`, with entities linked by edges. Deterministic string
|
|
34
|
+
* (no server LLM); the single source for the `structure_graph` MCP prompt.
|
|
35
|
+
* Triples-first so edges are unavoidable.
|
|
36
|
+
*/
|
|
37
|
+
declare function buildGraphStructuringPrompt(content: string): string;
|
|
38
|
+
declare function buildAgentSnippet(): string;
|
|
39
|
+
|
|
40
|
+
export { AGENT_TARGETS, PERSISTENT_MEMORY_SECTION, PYX_MEMORY_INSTRUCTIONS, SEARCH_ENUMERATION_CONCEPT_DESC, SEARCH_LIMIT_DESC, STORE_ENTITIES_DESC, STORE_EVENT_TIME_DESC, STORE_RELATIONSHIPS_DESC, STORE_TOOL_DESC, STORE_TRIPLES_DESC, STRUCTURE_GRAPH_PROMPT_DESC, buildAgentSnippet, buildDesignGuide, buildGraphStructuringPrompt };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGENT_TARGETS,
|
|
3
|
+
PERSISTENT_MEMORY_SECTION,
|
|
4
|
+
PYX_MEMORY_INSTRUCTIONS,
|
|
5
|
+
SEARCH_ENUMERATION_CONCEPT_DESC,
|
|
6
|
+
SEARCH_LIMIT_DESC,
|
|
7
|
+
STORE_ENTITIES_DESC,
|
|
8
|
+
STORE_EVENT_TIME_DESC,
|
|
9
|
+
STORE_RELATIONSHIPS_DESC,
|
|
10
|
+
STORE_TOOL_DESC,
|
|
11
|
+
STORE_TRIPLES_DESC,
|
|
12
|
+
STRUCTURE_GRAPH_PROMPT_DESC,
|
|
13
|
+
buildAgentSnippet,
|
|
14
|
+
buildDesignGuide,
|
|
15
|
+
buildGraphStructuringPrompt
|
|
16
|
+
} from "./chunk-XHEVB23R.mjs";
|
|
17
|
+
export {
|
|
18
|
+
AGENT_TARGETS,
|
|
19
|
+
PERSISTENT_MEMORY_SECTION,
|
|
20
|
+
PYX_MEMORY_INSTRUCTIONS,
|
|
21
|
+
SEARCH_ENUMERATION_CONCEPT_DESC,
|
|
22
|
+
SEARCH_LIMIT_DESC,
|
|
23
|
+
STORE_ENTITIES_DESC,
|
|
24
|
+
STORE_EVENT_TIME_DESC,
|
|
25
|
+
STORE_RELATIONSHIPS_DESC,
|
|
26
|
+
STORE_TOOL_DESC,
|
|
27
|
+
STORE_TRIPLES_DESC,
|
|
28
|
+
STRUCTURE_GRAPH_PROMPT_DESC,
|
|
29
|
+
buildAgentSnippet,
|
|
30
|
+
buildDesignGuide,
|
|
31
|
+
buildGraphStructuringPrompt
|
|
32
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
mergeExtractedEntities
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-X6AYWXW7.mjs";
|
|
4
4
|
|
|
5
5
|
// ../client/src/memory-client.ts
|
|
6
6
|
var MemoryServerError = class extends Error {
|
|
@@ -57,12 +57,6 @@ var MemoryClient = class {
|
|
|
57
57
|
async store(entry, options) {
|
|
58
58
|
const callback = options?.enrichment?.extractEntities;
|
|
59
59
|
const optedOut = entry.extractEntities === false;
|
|
60
|
-
const wantsForced = entry.extractEntities === true;
|
|
61
|
-
if (wantsForced && !callback) {
|
|
62
|
-
throw new Error(
|
|
63
|
-
"extractEntities=true requested but no enrichment.extractEntities callback was provided. Pass options.enrichment.extractEntities, or set entry.extractEntities=false to skip."
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
60
|
let payload = entry;
|
|
67
61
|
if (callback && !optedOut) {
|
|
68
62
|
const extracted = await callback({
|
|
@@ -10,6 +10,16 @@ function normalizeGraphLabel(value, fallback) {
|
|
|
10
10
|
const normalized = value.trim().toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
11
11
|
return normalized.length > 0 ? normalized : fallback;
|
|
12
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
|
+
}
|
|
13
23
|
function mergeExtractedEntities(callerEntities, callerRelationships, extracted) {
|
|
14
24
|
const entities = [...callerEntities ?? []];
|
|
15
25
|
const relationships = [...callerRelationships ?? []];
|
|
@@ -36,7 +46,15 @@ function mergeExtractedEntities(callerEntities, callerRelationships, extracted)
|
|
|
36
46
|
});
|
|
37
47
|
}
|
|
38
48
|
}
|
|
39
|
-
|
|
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 };
|
|
40
58
|
}
|
|
41
59
|
|
|
42
60
|
// ../shared/src/types/isolation.ts
|
|
@@ -110,6 +128,7 @@ var SINGLE_TENANT_ID = "_single";
|
|
|
110
128
|
export {
|
|
111
129
|
DEFAULTS,
|
|
112
130
|
normalizeGraphLabel,
|
|
131
|
+
normalizeNameKey,
|
|
113
132
|
mergeExtractedEntities,
|
|
114
133
|
NamespaceIsolation,
|
|
115
134
|
MemoryType,
|
|
@@ -0,0 +1,169 @@
|
|
|
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.`;
|
|
3
|
+
var PERSISTENT_MEMORY_SECTION = [
|
|
4
|
+
"## Persistent Memory",
|
|
5
|
+
"",
|
|
6
|
+
"Use the configured `pyx-memory` MCP tools for durable memory across conversations.",
|
|
7
|
+
"",
|
|
8
|
+
"Search before assuming:",
|
|
9
|
+
"",
|
|
10
|
+
"- At the start of a new conversation or task, search for the project name and task topic.",
|
|
11
|
+
"- Before suggesting an approach, check whether prior decisions contradict it.",
|
|
12
|
+
"- When the user references prior work, search for it.",
|
|
13
|
+
"- When investigating a bug, search for prior occurrences.",
|
|
14
|
+
'- When a question names a time \u2014 explicit or relative ("last year", "\uB450 \uB2EC \uC804") \u2014 resolve it to an absolute ISO-8601 timestamp and pass it as search `anchorTime`: results rank by proximity to that time instead of now, without excluding anything.',
|
|
15
|
+
"- Match search `effort` to the task: `quick` (default \u2014 the strongest, most-used memories) for routine recall; `deep` (everything, including archived and superseded) when `quick` returns nothing or you need full history.",
|
|
16
|
+
'- To trace how a fact changed over time ("what did X use before Y", how it evolved), call `lineage` \u2014 pass `subject` + `relation` for graph lineage, or an `entryId` for a superseded chain \u2014 instead of stitching together multiple searches.',
|
|
17
|
+
"",
|
|
18
|
+
"Reinforce what you use:",
|
|
19
|
+
"",
|
|
20
|
+
"- After a retrieved memory actually informs your work (you cited it or acted on it), call `reinforce` on that memory. Recall is what keeps a memory strong: without it, memories fade out of the `quick`/`medium` tiers over time (idle never revives them \u2014 only reinforcement does).",
|
|
21
|
+
"",
|
|
22
|
+
"Apply corrections instead of repeating mistakes:",
|
|
23
|
+
"",
|
|
24
|
+
"- When the user corrects a mistake you made, call `record_correction` (what was wrong, what to do instead, and when it applies). Before starting a task, call `fetch_applicable_corrections` with the task shape 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` (private namespace per identity; grant everyone for shared). `callerAccessLevel` is sensitivity redaction, not isolation. Trust boundary: stdio/self-host/companion callers may pass scope per call; hosted MaaS/remote MCP derive identity from the project/token and strip or ignore caller-supplied identity until verified-JWT multi-tenant remote MCP exists.",
|
|
25
|
+
"",
|
|
26
|
+
"Store immediately after:",
|
|
27
|
+
"",
|
|
28
|
+
"- User corrections or workflow/tool preferences.",
|
|
29
|
+
"- Bug fixes, with root cause and solution.",
|
|
30
|
+
"- Architecture or design decisions, with reasoning.",
|
|
31
|
+
"- Integration details such as API format, auth flow, endpoint behavior, or tool behavior.",
|
|
32
|
+
"- Gotchas or pitfalls, with what went wrong and the fix.",
|
|
33
|
+
'- Explicit "remember this" requests.',
|
|
34
|
+
"",
|
|
35
|
+
"Memory rules:",
|
|
36
|
+
"",
|
|
37
|
+
"- Always include `topic` and `project`.",
|
|
38
|
+
"- Store concise facts and decisions, not deliberation.",
|
|
39
|
+
"- Keep one memory per concept \u2014 do not bundle unrelated facts.",
|
|
40
|
+
"- Do not store ephemeral file contents, current state, or in-progress work.",
|
|
41
|
+
'- 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; facts without `eventTime` cannot be ordered in time.',
|
|
42
|
+
'- Graph is a relational retrieval dimension, not a boost. 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. Relationships (edges) matter as much as entities because traversal needs edges to connect related memories. Entity-free memories are valid; zero graph writes are reported as counts. 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 (`"yoga"` IS_A `"fitness classes"`). 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. On count/list searches, pass `enumerationConcept` as the language-agnostic/global category phrase; the embedding model resolves the caller-supplied hint across a broad range of languages, and English/Korean marker auto-detect is only a fallback.',
|
|
43
|
+
"- Ingest documents/images only when they are worth persisting; images require a",
|
|
44
|
+
" short description so they are searchable."
|
|
45
|
+
].join("\n");
|
|
46
|
+
function buildDesignGuide({ appName }) {
|
|
47
|
+
const name = appName.trim() || "your app";
|
|
48
|
+
return [
|
|
49
|
+
`# ${name} pyx-memory Design Guide`,
|
|
50
|
+
"",
|
|
51
|
+
"This guide is generated from the canonical pyx-memory agent contract. Keep the global rules intact, then fill in the app-specific policy sections below.",
|
|
52
|
+
"",
|
|
53
|
+
PERSISTENT_MEMORY_SECTION,
|
|
54
|
+
"",
|
|
55
|
+
`## ${name} Fill-In Scaffold`,
|
|
56
|
+
"",
|
|
57
|
+
"### What IS Worth Remembering In This App",
|
|
58
|
+
"",
|
|
59
|
+
"- [ ] Durable user preferences, decisions, facts, corrections, workflows, and integration details that should survive across sessions.",
|
|
60
|
+
"- [ ] Bug fixes or operational gotchas only after the root cause and fix are known.",
|
|
61
|
+
"",
|
|
62
|
+
"### What NOT To Store",
|
|
63
|
+
"",
|
|
64
|
+
"- [ ] Ephemeral UI state, transient progress, raw logs, draft deliberation, duplicate facts, or data that belongs in the product database instead of the memory layer.",
|
|
65
|
+
"- [ ] Secrets, credentials, payment data, private keys, or sensitive payloads unless your app has an explicit policy and encryption posture for them.",
|
|
66
|
+
"",
|
|
67
|
+
"### eventTime Rules",
|
|
68
|
+
"",
|
|
69
|
+
"- [ ] Pass `eventTime` as an ISO-8601 timestamp for any fact whose ordering can change the answer later.",
|
|
70
|
+
"- [ ] Use the time the fact happened or took effect, not the time the agent noticed it.",
|
|
71
|
+
"",
|
|
72
|
+
"### Entity And Relationship Expectations",
|
|
73
|
+
"",
|
|
74
|
+
"- [ ] When content names people, organizations, tools, places, events, or key concepts, pass both `entities` and `relationships` when the caller already knows them.",
|
|
75
|
+
"- [ ] Treat graph edges as retrieval structure, not decoration; add `IS_A` category edges for count/list questions users may ask later.",
|
|
76
|
+
"- [ ] The calling agent builds the graph: pass `entities` + `relationships` (or `triples`), and connect multi-entity stores with at least one relationship. Server-side extraction is OFF by default \u2014 opt in only by configuring a self-host BYO extraction endpoint.",
|
|
77
|
+
"",
|
|
78
|
+
"### Privacy And Sensitivity",
|
|
79
|
+
"",
|
|
80
|
+
"- [ ] Define which memory topics are public, internal, or secret for this app.",
|
|
81
|
+
"- [ ] Decide what must be blocked, redacted, encrypted, or never sent to an external extraction endpoint.",
|
|
82
|
+
"",
|
|
83
|
+
"### Image Description Policy",
|
|
84
|
+
"",
|
|
85
|
+
"- [ ] Images require caller-provided descriptions from your app or agent vision hook before they become semantically searchable.",
|
|
86
|
+
"- [ ] The thin SDK preset does not understand images; it only creates an HTTP `MemoryClient`.",
|
|
87
|
+
"",
|
|
88
|
+
"### Parity Scope",
|
|
89
|
+
"",
|
|
90
|
+
"- [ ] pyx-memory parity is the memory layer: the enrichment/file-memory glue collapses into this generated contract and the HTTP client. Product-specific orchestration still belongs to the app."
|
|
91
|
+
].join("\n");
|
|
92
|
+
}
|
|
93
|
+
var AGENT_TARGETS = [
|
|
94
|
+
{ tool: "Claude Code", instructionsFile: "~/.claude/CLAUDE.md" },
|
|
95
|
+
{ tool: "Codex CLI", instructionsFile: "~/.codex/AGENTS.md" },
|
|
96
|
+
{ tool: "oh-my-pi (omp)", instructionsFile: "~/.omp/agent/RULES.md" },
|
|
97
|
+
{ tool: "Gemini CLI", instructionsFile: "~/.gemini/GEMINI.md" },
|
|
98
|
+
{
|
|
99
|
+
tool: "Any other tool",
|
|
100
|
+
instructionsFile: "that tool's user-scope (global) instructions/rules file"
|
|
101
|
+
}
|
|
102
|
+
];
|
|
103
|
+
var STORE_ENTITIES_DESC = "Named entities mentioned by the content. 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.";
|
|
104
|
+
var STORE_RELATIONSHIPS_DESC = 'Edges between entities; source and target must be entity names from this request. 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. Relationships matter as much as entities because graph traversal needs edges to 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 un-enumerated count-noun variants at query time via strict embedding over memberful CONCEPT nodes.';
|
|
105
|
+
var STORE_TRIPLES_DESC = "Graph facts as subject\u2013relation\u2013object triples; each is materialized into the two entity nodes plus the edge that connects them, so a node cannot be added without its relationship. Use this (or entities + relationships) to populate the knowledge graph \u2014 for weak hosts triples make edges unavoidable. subject/object each have a name and a type (from the entity-type set); relation is a freeform label. Merged with any entities/relationships you also pass (caller-wins); duplicates within one call are collapsed.";
|
|
106
|
+
var STORE_EVENT_TIME_DESC = 'ISO-8601 time the fact happened or took effect (bi-temporal). Pass for any fact that can change or go stale \u2014 recency ordering, dated ("as of") queries, and stale-vs-current conflict resolution key off it.';
|
|
107
|
+
var STORE_TOOL_DESC = "Store one concise factual memory with required topic and project metadata. 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. Relationships (edges) matter as much as entities because graph traversal needs edges to connect related memories. Entity-free memories are valid; omit graph data or set extractEntities:false when there are no graph facts.";
|
|
108
|
+
var SEARCH_LIMIT_DESC = "Target result count; server clamps to 1\u2013100. Default 10. The hybrid strategy may widen count/list searches up to 30 for set-completeness recall when the caller passes `enumerationConcept`; English/Korean marker auto-detect is a best-effort fallback.";
|
|
109
|
+
var SEARCH_ENUMERATION_CONCEPT_DESC = 'For count/list questions about a category, pass the language-agnostic/global category phrase here (for example, "fitness classes" or "\uC6B4\uB3D9 \uC218\uC5C5"). The embedding model resolves the caller-supplied hint across a broad range of languages.';
|
|
110
|
+
var STRUCTURE_GRAPH_PROMPT_DESC = "Returns a prompt that extracts the graph fields (triples, or entities + relationships) to include in a store_memory call \u2014 run it with your own LLM, then add the JSON it returns to your store_memory arguments (you still supply content/topic/project). Useful when store_memory returns GRAPH_RELATIONSHIPS_REQUIRED, or for any content with graph-worthy facts. This prompt is executed by your agent, not the server.";
|
|
111
|
+
function buildGraphStructuringPrompt(content) {
|
|
112
|
+
return [
|
|
113
|
+
"Read the content below and produce the GRAPH FIELDS to add to your store_memory call (you supply content/topic/project yourself). Output ONLY the JSON for those fields, no prose.",
|
|
114
|
+
"",
|
|
115
|
+
"Prefer triples \u2014 they make edges impossible to forget:",
|
|
116
|
+
' { "triples": [ { "subject": { "name": "...", "type": "<TYPE>" }, "relation": "<UPPER_SNAKE>", "object": { "name": "...", "type": "<TYPE>" } } ] }',
|
|
117
|
+
'Equivalent shape: { "entities": [ { "name": "...", "type": "<TYPE>" } ], "relationships": [ { "source": "...", "target": "...", "type": "<UPPER_SNAKE>" } ] }',
|
|
118
|
+
"",
|
|
119
|
+
"<TYPE> is one of: PERSON, ORGANIZATION, CONCEPT, TOOL, LOCATION, EVENT.",
|
|
120
|
+
"relation/type is a freeform UPPER_SNAKE label (e.g. WORKS_AT, USES, OWNS, PART_OF, IS_A, CREATED_BY).",
|
|
121
|
+
"",
|
|
122
|
+
"Rules:",
|
|
123
|
+
"- Link every entity you emit with at least one non-self edge \u2014 isolated nodes never connect. If two are only loosely related, use RELATED_TO rather than leaving one unconnected.",
|
|
124
|
+
"- Do not invent facts beyond the content.",
|
|
125
|
+
"",
|
|
126
|
+
"Content:",
|
|
127
|
+
'"""',
|
|
128
|
+
content,
|
|
129
|
+
'"""'
|
|
130
|
+
].join("\n");
|
|
131
|
+
}
|
|
132
|
+
function buildAgentSnippet() {
|
|
133
|
+
const targetLines = AGENT_TARGETS.map(
|
|
134
|
+
({ tool, instructionsFile }) => `- ${tool} \u2192 ${instructionsFile}`
|
|
135
|
+
);
|
|
136
|
+
return [
|
|
137
|
+
"AI agent: install the global memory rules below \u2014 make no other changes.",
|
|
138
|
+
"",
|
|
139
|
+
"Choose the target file by the HOST tool you are running inside, NOT by which model you are. A Claude, GPT, or Gemini model running inside another agent (oh-my-pi, Cursor, Cline, \u2026) must write to THAT agent's file \u2014 never to Claude Code's ~/.claude/CLAUDE.md or its model provider's file. If you are not certain which file your tool uses, ask the user.",
|
|
140
|
+
"",
|
|
141
|
+
"Target file by tool:",
|
|
142
|
+
...targetLines,
|
|
143
|
+
"",
|
|
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.",
|
|
146
|
+
"- 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
|
+
"Install only the section below, verbatim. After writing, tell the user which file you updated.",
|
|
149
|
+
"",
|
|
150
|
+
PERSISTENT_MEMORY_SECTION
|
|
151
|
+
].join("\n");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export {
|
|
155
|
+
PYX_MEMORY_INSTRUCTIONS,
|
|
156
|
+
PERSISTENT_MEMORY_SECTION,
|
|
157
|
+
buildDesignGuide,
|
|
158
|
+
AGENT_TARGETS,
|
|
159
|
+
STORE_ENTITIES_DESC,
|
|
160
|
+
STORE_RELATIONSHIPS_DESC,
|
|
161
|
+
STORE_TRIPLES_DESC,
|
|
162
|
+
STORE_EVENT_TIME_DESC,
|
|
163
|
+
STORE_TOOL_DESC,
|
|
164
|
+
SEARCH_LIMIT_DESC,
|
|
165
|
+
SEARCH_ENUMERATION_CONCEPT_DESC,
|
|
166
|
+
STRUCTURE_GRAPH_PROMPT_DESC,
|
|
167
|
+
buildGraphStructuringPrompt,
|
|
168
|
+
buildAgentSnippet
|
|
169
|
+
};
|