@pyxmate/memory 1.22.3 → 1.23.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,6 +1,7 @@
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 when durable state settles \u2014 usually before your final response or handoff; if nothing durable changed, store nothing. Otherwise capture a fact (correction, bug fix + root cause, design/architecture decision + reasoning, integration/API/auth/endpoint detail, gotcha, preference, \"remember this\"), a reusable process once stable, a milestone snapshot (type episodic, not a running log), or a changed decision (store the new state with eventTime, never a silent overwrite, so lineage traces it) \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 tenant/namespace isolation is enforced server-side (namespaces + ReBAC grants; see docs/access-control.md).";
1
+ declare const PYX_MEMORY_INSTRUCTIONS = "Use pyx-memory for durable memory across sessions, proactively \u2014 do not wait to be told. It is the memory of record: a host's built-in memory (e.g. MEMORY.md) never replaces it. At the start of each conversation, load the tools (even if deferred) and call get_memory_rules; its rules are current and supersede any older pasted copy. 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. STORE when durable state settles \u2014 usually before your final response or handoff; if nothing durable changed, store nothing. Otherwise capture a fact (correction, bug fix + root cause, design decision + reasoning, integration/API detail, gotcha, preference, \"remember this\"), a reusable process once stable, a milestone snapshot (type episodic, not a running log), or a changed decision (store the new state with eventTime, never a silent overwrite, so lineage traces it) \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. When the user corrects you, call record_correction; before a task, call fetch_applicable_corrections. 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, not a security isolation boundary.";
2
2
  declare const PYX_MEMORY_COMPANION_CORE_INSTRUCTIONS = "Use pyx-memory for durable memory across sessions, proactively \u2014 do not wait to be told. SEARCH (search_memories) before assuming: at the start of a task, and before 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. Match search effort to need: quick (default, strongest) for routine recall, deep for full/archived history. STORE (store_memory) when durable state settles \u2014 usually before your final response; if nothing durable changed, store nothing. Capture concise facts, not deliberation \u2014 a correction, bug fix + root cause, decision + reasoning, integration detail, gotcha, preference, or an explicit \"remember this\" \u2014 each with topic and project. Pass eventTime (ISO-8601, when the fact took effect) for anything that can change or go stale; recency ordering and stale-vs-current resolution key off it. When content names people, organizations, tools, places, events, or key concepts, pass entities and relationships \u2014 you build the graph; a multi-entity store with no connecting edge is refused. Recalled memories reflect what was true when written \u2014 verify named files, flags, and versions before acting.";
3
3
  declare const PERSISTENT_MEMORY_SECTION: string;
4
+ declare const PASTE_MEMORY_RULES: string;
4
5
  declare function buildDesignGuide({ appName }: {
5
6
  appName: string;
6
7
  }): string;
@@ -38,4 +39,4 @@ declare const STRUCTURE_GRAPH_PROMPT_DESC = "Returns a prompt that extracts the
38
39
  declare function buildGraphStructuringPrompt(content: string): string;
39
40
  declare function buildAgentSnippet(): string;
40
41
 
41
- export { AGENT_TARGETS, PERSISTENT_MEMORY_SECTION, PYX_MEMORY_COMPANION_CORE_INSTRUCTIONS, 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 };
42
+ export { AGENT_TARGETS, PASTE_MEMORY_RULES, PERSISTENT_MEMORY_SECTION, PYX_MEMORY_COMPANION_CORE_INSTRUCTIONS, 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 };
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  AGENT_TARGETS,
3
+ PASTE_MEMORY_RULES,
3
4
  PERSISTENT_MEMORY_SECTION,
4
5
  PYX_MEMORY_COMPANION_CORE_INSTRUCTIONS,
5
6
  PYX_MEMORY_INSTRUCTIONS,
@@ -14,9 +15,10 @@ import {
14
15
  buildAgentSnippet,
15
16
  buildDesignGuide,
16
17
  buildGraphStructuringPrompt
17
- } from "./chunk-EZERG25I.mjs";
18
+ } from "./chunk-QD7G23CN.mjs";
18
19
  export {
19
20
  AGENT_TARGETS,
21
+ PASTE_MEMORY_RULES,
20
22
  PERSISTENT_MEMORY_SECTION,
21
23
  PYX_MEMORY_COMPANION_CORE_INSTRUCTIONS,
22
24
  PYX_MEMORY_INSTRUCTIONS,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  MemoryClient
3
- } from "./chunk-HVJZDJGH.mjs";
3
+ } from "./chunk-SGL73IY5.mjs";
4
4
 
5
5
  // ../dashboard/src/aggregations/consolidation-analytics.ts
6
6
  function analyzeConsolidationLog(entries) {
@@ -256,6 +256,11 @@ var DATA_PLANE_OPERATION_COVERAGE = {
256
256
  reasonCode: "nonchargeable_operation",
257
257
  reason: "status is nonchargeable and must bypass data-plane reservation"
258
258
  },
259
+ "mcp:get_memory_rules": {
260
+ support: "not_execution_ready",
261
+ reasonCode: "nonchargeable_operation",
262
+ reason: "the rules text is nonchargeable and must bypass data-plane reservation"
263
+ },
259
264
  "mcp:get_user_profile": { support: "sqlite_atomic_one_step" },
260
265
  "mcp:upsert_user_profile": { support: "sqlite_atomic_one_step" },
261
266
  "mcp:record_correction": { support: "sqlite_atomic_one_step" },
@@ -1,10 +1,10 @@
1
1
  // src/contract/index.ts
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 when durable state settles \u2014 usually before your final response or handoff; if nothing durable changed, store nothing. Otherwise capture a fact (correction, bug fix + root cause, design/architecture decision + reasoning, integration/API/auth/endpoint detail, gotcha, preference, "remember this"), a reusable process once stable, a milestone snapshot (type episodic, not a running log), or a changed decision (store the new state with eventTime, never a silent overwrite, so lineage traces it) \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 tenant/namespace isolation is enforced server-side (namespaces + ReBAC grants; see docs/access-control.md).`;
2
+ var PYX_MEMORY_INSTRUCTIONS = `Use pyx-memory for durable memory across sessions, proactively \u2014 do not wait to be told. It is the memory of record: a host's built-in memory (e.g. MEMORY.md) never replaces it. At the start of each conversation, load the tools (even if deferred) and call get_memory_rules; its rules are current and supersede any older pasted copy. 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. STORE when durable state settles \u2014 usually before your final response or handoff; if nothing durable changed, store nothing. Otherwise capture a fact (correction, bug fix + root cause, design decision + reasoning, integration/API detail, gotcha, preference, "remember this"), a reusable process once stable, a milestone snapshot (type episodic, not a running log), or a changed decision (store the new state with eventTime, never a silent overwrite, so lineage traces it) \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. When the user corrects you, call record_correction; before a task, call fetch_applicable_corrections. 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, not a security isolation boundary.`;
3
3
  var PYX_MEMORY_COMPANION_CORE_INSTRUCTIONS = `Use pyx-memory for durable memory across sessions, proactively \u2014 do not wait to be told. SEARCH (search_memories) before assuming: at the start of a task, and before 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. Match search effort to need: quick (default, strongest) for routine recall, deep for full/archived history. STORE (store_memory) when durable state settles \u2014 usually before your final response; if nothing durable changed, store nothing. Capture concise facts, not deliberation \u2014 a correction, bug fix + root cause, decision + reasoning, integration detail, gotcha, preference, or an explicit "remember this" \u2014 each with topic and project. Pass eventTime (ISO-8601, when the fact took effect) for anything that can change or go stale; recency ordering and stale-vs-current resolution key off it. When content names people, organizations, tools, places, events, or key concepts, pass entities and relationships \u2014 you build the graph; a multi-entity store with no connecting edge is refused. Recalled memories reflect what was true when written \u2014 verify named files, flags, and versions before acting.`;
4
4
  var PERSISTENT_MEMORY_SECTION = [
5
5
  "## Persistent Memory",
6
6
  "",
7
- "Use the configured `pyx-memory` MCP tools for durable memory across conversations.",
7
+ "Use the configured `pyx-memory` MCP tools as your memory of record across conversations; a host's built-in memory (e.g. `MEMORY.md`) never replaces them. Load them first if deferred.",
8
8
  "",
9
9
  "Search before assuming:",
10
10
  "",
@@ -28,7 +28,7 @@ var PERSISTENT_MEMORY_SECTION = [
28
28
  "Store when durable state settles, usually before a final response/handoff; if nothing durable changed, store nothing. Capture:",
29
29
  "",
30
30
  '- Facts (default) \u2014 one correction/preference, root-cause fix, reasoned design decision, integration detail, gotcha, or explicit "remember this" request per memory.',
31
- "- Process (`type: long-term`) \u2014 a reusable workflow, convention, or playbook a fresh session should follow. Store it once stabilized or adopted.",
31
+ "- Process (`type: long-term`) \u2014 a reusable workflow, convention, or playbook a fresh session should follow, once stabilized or adopted.",
32
32
  "- Trajectory (`type: episodic`) \u2014 only at a real milestone, handoff, or user-requested checkpoint: one dated snapshot of what changed, where it stands, and the open frontier. Not a running log.",
33
33
  "- Ordinary conversational facts omit `id` and evolve through new entries with `eventTime`. Only application-owned canonical mirrors may reuse an app-supplied stable `id` with `metadata.dbRef.revision`. Revisions are opaque; retry with the same stable `id`, opaque revision, and byte-exact identical payload. Never invent, randomize, or timestamp-suffix IDs; replay stale snapshots or manifests; or use store-new plus delete-old. On `revision_reuse_conflict`, reread the authoritative source; do not mint a new ID or revision.",
34
34
  "",
@@ -36,15 +36,19 @@ var PERSISTENT_MEMORY_SECTION = [
36
36
  "",
37
37
  "- Always include `topic` and `project`.",
38
38
  "- Store concise facts and decisions, not deliberation.",
39
- "- Keep one memory per concept; do not bundle unrelated facts.",
40
39
  "- Do not store ephemeral files, raw logs, or progress. Aggregate high-frequency streams into a dated fact/summary with `eventTime`; never one memory or graph node per sample.",
41
40
  "- Pass `eventTime` (ISO-8601, when a changeable/stale fact happened/took effect); recency/as-of/stale-vs-current key off it.",
42
41
  "- Graph is for retrieval. For named people, organizations, tools, places, events, or concepts, pass `entities` + `relationships` (caller-wins); connect multi-entity stores with a relationship or `triples`. Relationships matter as much as entities. Entity-free memories are valid.",
43
42
  '- Use specific durable entities, never catch-all hubs ("me", "health"); bounded `IS_A` categories are the exception.',
44
43
  '- For countable categories the user may later enumerate, add a canonical category `CONCEPT` node and an `IS_A` edge from each member to it (`"yoga"` IS_A `"fitness classes"`).',
45
44
  "- Taxonomy: when a store response reports unnamed clusters, call get_taxonomy_state and name each via name_cluster (short, domain-true, user's language); rename only when composition clearly changed.",
46
- "- Ingest documents/images only when they are worth persisting; images require a short description so they are searchable.",
47
- "- Before ingesting docx/xlsx/pptx, prefer pre-extracting to `.md`; headings preserve section context."
45
+ "- Ingest documents/images only when worth persisting; images need a short description to be searchable.",
46
+ "- Before ingesting docx/xlsx/pptx, pre-extract to `.md`; headings keep section context."
47
+ ].join("\n");
48
+ var PASTE_MEMORY_RULES = [
49
+ "## Persistent Memory",
50
+ "",
51
+ "Use the configured `pyx-memory` MCP tools as your memory of record across conversations; a host's built-in memory never replaces them. At the start of every conversation, load them (even if deferred), call `get_memory_rules`, and follow the rules it returns \u2014 they are always current."
48
52
  ].join("\n");
49
53
  function buildDesignGuide({ appName }) {
50
54
  const name = appName.trim() || "your app";
@@ -154,7 +158,7 @@ function buildAgentSnippet() {
154
158
  "",
155
159
  "Install only the section below, verbatim. After writing, tell the user which file you updated.",
156
160
  "",
157
- PERSISTENT_MEMORY_SECTION
161
+ PASTE_MEMORY_RULES
158
162
  ].join("\n");
159
163
  }
160
164
 
@@ -162,6 +166,7 @@ export {
162
166
  PYX_MEMORY_INSTRUCTIONS,
163
167
  PYX_MEMORY_COMPANION_CORE_INSTRUCTIONS,
164
168
  PERSISTENT_MEMORY_SECTION,
169
+ PASTE_MEMORY_RULES,
165
170
  buildDesignGuide,
166
171
  AGENT_TARGETS,
167
172
  STORE_ENTITIES_DESC,
@@ -2,7 +2,7 @@ import {
2
2
  RAGStrategy,
3
3
  assertGraphExtractionPayload,
4
4
  mergeExtractedEntities
5
- } from "./chunk-BUKDWHTY.mjs";
5
+ } from "./chunk-C46EE5BH.mjs";
6
6
 
7
7
  // ../client/src/disabled-memory.ts
8
8
  var DEFAULT_PAGE_LIMIT = 20;
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  PERSISTENT_MEMORY_SECTION,
4
4
  buildDesignGuide
5
- } from "../chunk-EZERG25I.mjs";
5
+ } from "../chunk-QD7G23CN.mjs";
6
6
  import {
7
7
  MEMORY_PROJECT_LABEL_MAX_CHARS,
8
8
  TAXONOMY_MAX_CATEGORIES,
@@ -10,7 +10,7 @@ import {
10
10
  TAXONOMY_MAX_SAMPLE_TOPICS,
11
11
  TAXONOMY_MAX_TOP_ENTITIES
12
12
  } from "../chunk-KZKGRND2.mjs";
13
- import "../chunk-BUKDWHTY.mjs";
13
+ import "../chunk-C46EE5BH.mjs";
14
14
 
15
15
  // src/cli/exit-codes.ts
16
16
  var EXIT = {
@@ -2223,7 +2223,7 @@ function createProxyServer(client, version, uploadLocalFile) {
2223
2223
  return server;
2224
2224
  }
2225
2225
  async function runMcpProxyServer(opts) {
2226
- const version = opts.version ?? (true ? "1.22.3" : "0.0.0-dev");
2226
+ const version = opts.version ?? (true ? "1.23.0" : "0.0.0-dev");
2227
2227
  const read = await opts.readCredentials();
2228
2228
  if (!read.ok) {
2229
2229
  throw new Error(
@@ -2257,7 +2257,9 @@ async function runMcpProxyServer(opts) {
2257
2257
  const closed = new Promise((resolve3) => {
2258
2258
  transport.onclose = () => resolve3();
2259
2259
  });
2260
+ process.stdin.once("end", () => void transport.close());
2260
2261
  await server.connect(transport);
2262
+ void opts.onConnected?.(client);
2261
2263
  await closed;
2262
2264
  } finally {
2263
2265
  await client.close().catch(() => {
@@ -2591,17 +2593,26 @@ function mcpInstallCodexCommand(opts = {}) {
2591
2593
  const remote = opts.remote ?? false;
2592
2594
  const spawnSync = opts._spawnSync ?? nodeSpawnSync;
2593
2595
  const home = opts._homeDir ?? homedir2();
2596
+ const codexHome = (opts._env ?? process.env).CODEX_HOME;
2594
2597
  const probe = spawnSync("codex", ["--version"], { stdio: "pipe" });
2595
2598
  if (probe.error || probe.status !== 0) {
2596
- printCodexManualInstructions(home, remote);
2599
+ printCodexManualInstructions(home, remote, codexHome);
2597
2600
  return EXIT.OK;
2598
2601
  }
2599
- const cmd = ["mcp", "add", SERVER_NAME, "--", "pyx-mem", ...pyxMemArgs(remote)];
2602
+ const cmd = [
2603
+ "mcp",
2604
+ "add",
2605
+ SERVER_NAME,
2606
+ ...codexHome ? ["--env", `CODEX_HOME=${codexHome}`] : [],
2607
+ "--",
2608
+ "pyx-mem",
2609
+ ...pyxMemArgs(remote)
2610
+ ];
2600
2611
  const res = spawnSync("codex", cmd, { stdio: "inherit" });
2601
2612
  if (res.status !== 0) {
2602
2613
  process.stderr.write(`\`codex ${cmd.join(" ")}\` exited with code ${res.status ?? "null"}.
2603
2614
  `);
2604
- printCodexManualInstructions(home, remote);
2615
+ printCodexManualInstructions(home, remote, codexHome);
2605
2616
  return EXIT.USAGE;
2606
2617
  }
2607
2618
  process.stdout.write(
@@ -2611,20 +2622,21 @@ Restart Codex CLI to make the tools available. No API key was written to config.
2611
2622
  );
2612
2623
  return EXIT.OK;
2613
2624
  }
2614
- function printCodexManualInstructions(home, remote) {
2615
- const configPath = join2(home, ".codex", "config.toml");
2625
+ function printCodexManualInstructions(home, remote, codexHome) {
2626
+ const configPath = join2(codexHome ?? join2(home, ".codex"), "config.toml");
2616
2627
  process.stdout.write(
2617
2628
  [
2618
2629
  "The `codex` CLI is not on PATH, or the install command failed.",
2619
2630
  "",
2620
2631
  "Run this once Codex CLI is installed:",
2621
- ` codex mcp add ${SERVER_NAME} -- pyx-mem ${pyxMemArgs(remote).join(" ")}`,
2632
+ ` codex mcp add ${SERVER_NAME}${codexHome ? ` --env ${JSON.stringify(`CODEX_HOME=${codexHome}`)}` : ""} -- pyx-mem ${pyxMemArgs(remote).join(" ")}`,
2622
2633
  "",
2623
2634
  `Or add this section manually to ${configPath}:`,
2624
2635
  "",
2625
2636
  `[mcp_servers.${SERVER_NAME}]`,
2626
2637
  `command = "pyx-mem"`,
2627
2638
  `args = ${JSON.stringify(pyxMemArgs(remote))}`,
2639
+ ...codexHome ? [`env = { CODEX_HOME = ${JSON.stringify(codexHome)} }`] : [],
2628
2640
  "",
2629
2641
  "Do NOT add your pyx-memory API key here. Run `pyx-mem login` to store it in the OS credential store.",
2630
2642
  ""
@@ -2841,35 +2853,49 @@ function printHermesManualInstructions(remote) {
2841
2853
  );
2842
2854
  }
2843
2855
  var RULES_FILE_TARGETS = {
2844
- "claude-code": [".claude", "CLAUDE.md"],
2845
- codex: [".codex", "AGENTS.md"],
2846
- "gemini-cli": [".gemini", "GEMINI.md"],
2847
- "oh-my-pi": [".omp", "agent", "RULES.md"]
2856
+ "claude-code": { envDir: "CLAUDE_CONFIG_DIR", dir: [".claude"], file: "CLAUDE.md" },
2857
+ codex: { envDir: "CODEX_HOME", dir: [".codex"], file: "AGENTS.md" },
2858
+ "gemini-cli": { dir: [".gemini"], file: "GEMINI.md" },
2859
+ "oh-my-pi": { dir: [".omp", "agent"], file: "RULES.md" }
2848
2860
  };
2861
+ function rulesFilePath(target, opts) {
2862
+ if (!Object.hasOwn(RULES_FILE_TARGETS, target)) return void 0;
2863
+ const spec = RULES_FILE_TARGETS[target];
2864
+ const env = opts._env ?? (opts._homeDir === void 0 ? process.env : {});
2865
+ const configDir = spec.envDir ? env[spec.envDir] : void 0;
2866
+ return configDir ? join2(configDir, spec.file) : join2(opts._homeDir ?? homedir2(), ...spec.dir, spec.file);
2867
+ }
2849
2868
  function installMemoryRules(opts) {
2850
- const segs = RULES_FILE_TARGETS[opts.target];
2851
- if (!segs) return;
2869
+ const filePath = rulesFilePath(opts.target, opts);
2870
+ if (!filePath) return;
2852
2871
  if (opts.noRules) {
2853
2872
  process.stdout.write(
2854
2873
  'Skipped writing the memory rules (--no-rules). The tools are installed, but the agent will reach for memory only when you ask; add the "## Persistent Memory" rules yourself to change that.\n'
2855
2874
  );
2856
2875
  return;
2857
2876
  }
2858
- const home = opts._homeDir ?? homedir2();
2859
- const result = writeMemoryRulesFile({
2860
- filePath: join2(home, ...segs),
2861
- section: PERSISTENT_MEMORY_SECTION
2862
- });
2877
+ const result = writeMemoryRulesFile({ filePath, section: PERSISTENT_MEMORY_SECTION });
2863
2878
  process.stdout.write(result.message);
2864
2879
  }
2880
+ function installMemoryRulesOnly(target, opts = {}) {
2881
+ if (target === void 0 || !rulesFilePath(target, opts)) {
2882
+ process.stderr.write(
2883
+ `Error: \`pyx-mem mcp rules\` needs a host with a known rules file: ${Object.keys(RULES_FILE_TARGETS).join(", ")}.
2884
+ `
2885
+ );
2886
+ return EXIT.USAGE;
2887
+ }
2888
+ installMemoryRules({ ...opts, target, noRules: false });
2889
+ return EXIT.OK;
2890
+ }
2865
2891
  function reconcileMemoryRules(opts = {}) {
2866
- const home = opts._homeDir ?? homedir2();
2867
- for (const segs of Object.values(RULES_FILE_TARGETS)) {
2868
- const filePath = join2(home, ...segs);
2892
+ const section = opts.section ?? PERSISTENT_MEMORY_SECTION;
2893
+ for (const target of Object.keys(RULES_FILE_TARGETS)) {
2894
+ const filePath = rulesFilePath(target, opts);
2869
2895
  try {
2870
2896
  if (!existsSync3(filePath)) continue;
2871
2897
  if (!hasPyxMemoryRules(readFileSync3(filePath, "utf8"))) continue;
2872
- const result = writeMemoryRulesFile({ filePath, section: PERSISTENT_MEMORY_SECTION });
2898
+ const result = writeMemoryRulesFile({ filePath, section });
2873
2899
  if (result.action !== "unchanged") {
2874
2900
  process.stderr.write(
2875
2901
  `pyx-memory: refreshed the "## Persistent Memory" rules in ${filePath}
@@ -2880,6 +2906,31 @@ function reconcileMemoryRules(opts = {}) {
2880
2906
  }
2881
2907
  }
2882
2908
  }
2909
+ var MAX_SERVER_RULES_BYTES = 16 * 1024;
2910
+ async function syncMemoryRulesFromServer(client, opts = {}) {
2911
+ try {
2912
+ const { tools } = await client.listTools();
2913
+ if (!tools.some((tool) => tool.name === "get_memory_rules")) {
2914
+ reconcileMemoryRules(opts);
2915
+ return;
2916
+ }
2917
+ const result = await client.callTool({ name: "get_memory_rules", arguments: {} });
2918
+ const [first] = result.content ?? [];
2919
+ const section = first?.type === "text" ? first.text : void 0;
2920
+ if (result.isError || !section || !isRulesSection(section)) {
2921
+ throw new Error('the server returned no valid "## Persistent Memory" section');
2922
+ }
2923
+ reconcileMemoryRules({ ...opts, section });
2924
+ } catch (err) {
2925
+ process.stderr.write(
2926
+ `pyx-memory: left the installed memory rules unchanged \u2014 could not fetch current rules (${err instanceof Error ? err.message : String(err)}).
2927
+ `
2928
+ );
2929
+ }
2930
+ }
2931
+ function isRulesSection(text) {
2932
+ return text.startsWith("## Persistent Memory\n") && Buffer.byteLength(text, "utf8") <= MAX_SERVER_RULES_BYTES && !text.includes("<!-- pyx-memory:") && !/^\s*(```|~~~)/m.test(text);
2933
+ }
2883
2934
  function validateScope(scope) {
2884
2935
  if (VALID_SCOPES.has(scope)) return true;
2885
2936
  process.stderr.write(`Error: invalid --scope \`${scope}\`. Expected: local | user | project.
@@ -2909,14 +2960,12 @@ function writeJsonAndReport(filePath, agentLabel, entry, opts = {}) {
2909
2960
 
2910
2961
  // src/cli/commands/mcp.ts
2911
2962
  async function mcpCommand() {
2912
- reconcileMemoryRules();
2913
2963
  const readCredentials = createReadCredentials(() => getDefaultKeychain());
2914
- const onStdinEnd = new Promise((resolve3) => {
2915
- process.stdin.once("end", resolve3);
2916
- process.stdin.once("close", resolve3);
2917
- });
2918
2964
  try {
2919
- await Promise.race([runMcpProxyServer({ readCredentials }), onStdinEnd]);
2965
+ await runMcpProxyServer({
2966
+ readCredentials,
2967
+ onConnected: (client) => syncMemoryRulesFromServer(client)
2968
+ });
2920
2969
  return EXIT.OK;
2921
2970
  } catch (err) {
2922
2971
  process.stderr.write(`Internal error: ${err instanceof Error ? err.message : String(err)}
@@ -3370,6 +3419,10 @@ Commands:
3370
3419
  your other content is preserved) so memory fires by
3371
3420
  default. Pass --no-rules to skip that write.
3372
3421
  Targets: claude-code, codex, cursor, cline, continue, windsurf, gemini-cli, pi, oh-my-pi, openclaw, hermes.
3422
+ mcp rules <target> Write or refresh only the always-on rules for
3423
+ claude-code, codex, gemini-cli, or oh-my-pi (no MCP
3424
+ registration). Honors CLAUDE_CONFIG_DIR / CODEX_HOME;
3425
+ 'pyx-mem mcp' keeps them current from the server.
3373
3426
 
3374
3427
  Notes:
3375
3428
  - Credentials are stored only in the OS credential store (Keychain / libsecret / Credential Manager).
@@ -3473,6 +3526,13 @@ function runMcpCommand(parsed) {
3473
3526
  parsed.flags["no-rules"] === true
3474
3527
  );
3475
3528
  }
3529
+ if (parsed.subcommand === "rules") {
3530
+ if (Object.keys(parsed.flags).length > 0) {
3531
+ process.stderr.write("Error: `pyx-mem mcp rules <host>` takes no flags.\n");
3532
+ return EXIT.USAGE;
3533
+ }
3534
+ return installMemoryRulesOnly(parsed.positional[0]);
3535
+ }
3476
3536
  process.stderr.write(
3477
3537
  `Error: unknown subcommand \`mcp ${parsed.subcommand}\`. Run \`pyx-mem --help\`.
3478
3538
  `
@@ -11,10 +11,10 @@ import {
11
11
  toGraphologyFormat,
12
12
  transformGraphData,
13
13
  unreachableHealth
14
- } from "./chunk-QWLTZIIK.mjs";
15
- import "./chunk-HVJZDJGH.mjs";
14
+ } from "./chunk-7YHCNO3N.mjs";
15
+ import "./chunk-SGL73IY5.mjs";
16
16
  import "./chunk-KZKGRND2.mjs";
17
- import "./chunk-BUKDWHTY.mjs";
17
+ import "./chunk-C46EE5BH.mjs";
18
18
  export {
19
19
  DashboardClient,
20
20
  Poller,
@@ -186,6 +186,11 @@ declare const DATA_PLANE_OPERATION_COVERAGE: {
186
186
  readonly reasonCode: "nonchargeable_operation";
187
187
  readonly reason: "status is nonchargeable and must bypass data-plane reservation";
188
188
  };
189
+ readonly 'mcp:get_memory_rules': {
190
+ readonly support: "not_execution_ready";
191
+ readonly reasonCode: "nonchargeable_operation";
192
+ readonly reason: "the rules text is nonchargeable and must bypass data-plane reservation";
193
+ };
189
194
  readonly 'mcp:get_user_profile': {
190
195
  readonly support: "sqlite_atomic_one_step";
191
196
  };
@@ -1 +1 @@
1
- export { C as CanonicalDataPlaneObject, a as CanonicalDataPlaneValue, b as CompileContext, c as CompileDataPlaneRequest, d as CompileDataPlaneResult, f as CompiledDataPlaneExecutionPlan, D as DATA_PLANE_CATALOG_VERSION, g as DATA_PLANE_COMPILER_LIMITS, h as DATA_PLANE_CONTRACT_VERSION, i as DATA_PLANE_HTTP_RESPONSE_FRAME, j as DATA_PLANE_MANIFEST_FRAME, k as DATA_PLANE_MANIFEST_VERSION, l as DATA_PLANE_OPERATION_COVERAGE, m as DATA_PLANE_SEARCH_EMBEDDING_CONTRACT, n as DATA_PLANE_STEP_INPUT_FRAME, o as DataPlaneCatalogOperation, q as DataPlaneUnsupportedReasonCode, S as SupportedDataPlaneCatalogOperation, r as canonicalDataPlaneJson, s as compileDataPlaneRequest, t as hashDataPlaneHttpResponseV1, u as hashDataPlaneManifestV1, v as hashDataPlaneStepInputV1 } from './data-plane-contract-webbeMs5.js';
1
+ export { C as CanonicalDataPlaneObject, a as CanonicalDataPlaneValue, b as CompileContext, c as CompileDataPlaneRequest, d as CompileDataPlaneResult, f as CompiledDataPlaneExecutionPlan, D as DATA_PLANE_CATALOG_VERSION, g as DATA_PLANE_COMPILER_LIMITS, h as DATA_PLANE_CONTRACT_VERSION, i as DATA_PLANE_HTTP_RESPONSE_FRAME, j as DATA_PLANE_MANIFEST_FRAME, k as DATA_PLANE_MANIFEST_VERSION, l as DATA_PLANE_OPERATION_COVERAGE, m as DATA_PLANE_SEARCH_EMBEDDING_CONTRACT, n as DATA_PLANE_STEP_INPUT_FRAME, o as DataPlaneCatalogOperation, q as DataPlaneUnsupportedReasonCode, S as SupportedDataPlaneCatalogOperation, r as canonicalDataPlaneJson, s as compileDataPlaneRequest, t as hashDataPlaneHttpResponseV1, u as hashDataPlaneManifestV1, v as hashDataPlaneStepInputV1 } from './data-plane-contract-C5VqxrpF.js';
@@ -13,7 +13,7 @@ import {
13
13
  hashDataPlaneHttpResponseV1,
14
14
  hashDataPlaneManifestV1,
15
15
  hashDataPlaneStepInputV1
16
- } from "./chunk-BUKDWHTY.mjs";
16
+ } from "./chunk-C46EE5BH.mjs";
17
17
  export {
18
18
  DATA_PLANE_CATALOG_VERSION,
19
19
  DATA_PLANE_COMPILER_LIMITS,
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { StoreInput as StoreInput$1, MemoryEntry as MemoryEntry$1, MemorySearchParams as MemorySearchParams$1, MemorySearchResult as MemorySearchResult$1, MemoryType as MemoryType$1, PrincipalContext as PrincipalContext$1, SensitivityLevel as SensitivityLevel$1, MemoryStats as MemoryStats$1, MemoryInsights as MemoryInsights$1, LineageParams as LineageParams$1, LineageResult as LineageResult$1, ReinforceParams as ReinforceParams$1, ReinforceResult as ReinforceResult$1, WikiLintReport as WikiLintReport$1, GraphRepairResult as GraphRepairResult$1, ExtractedImageMeta as ExtractedImageMeta$1, IngestEntity as IngestEntity$1, IngestRelationship as IngestRelationship$1, EntityExtractionResult as EntityExtractionResult$1, Topology as Topology$1, IngestEvent as IngestEvent$1, GraphEnrichEvent as GraphEnrichEvent$1, GraphNode as GraphNode$1, GraphTraversalResult as GraphTraversalResult$1, CorrectionRecord as CorrectionRecord$1 } from '@pyx-memory/shared';
2
2
  export { documentContentSource, documentGraphSource, documentImageSource } from '@pyx-memory/shared';
3
- export { e as encodeListCursorToken, p as parseListCursorToken } from './data-plane-contract-webbeMs5.js';
3
+ export { e as encodeListCursorToken, p as parseListCursorToken } from './data-plane-contract-C5VqxrpF.js';
4
4
 
5
5
  /** ISO 8601 timestamp string */
6
6
  type Timestamp = string;
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  DisabledMemory,
3
3
  MemoryClient,
4
4
  MemoryServerError
5
- } from "./chunk-HVJZDJGH.mjs";
5
+ } from "./chunk-SGL73IY5.mjs";
6
6
  import {
7
7
  DEFAULTS,
8
8
  MEMORY_PROJECT_LABEL_MAX_CHARS,
@@ -37,7 +37,7 @@ import {
37
37
  normalizeGraphLabel,
38
38
  normalizeNameKey,
39
39
  parseListCursorToken
40
- } from "./chunk-BUKDWHTY.mjs";
40
+ } from "./chunk-C46EE5BH.mjs";
41
41
 
42
42
  // src/preset.ts
43
43
  var DEFAULT_MEMORY_URL = `http://localhost:${DEFAULTS.MEMORY_SERVER_PORT}`;
package/dist/react.mjs CHANGED
@@ -11,10 +11,10 @@ import {
11
11
  toGraphologyFormat,
12
12
  transformGraphData,
13
13
  unreachableHealth
14
- } from "./chunk-QWLTZIIK.mjs";
15
- import "./chunk-HVJZDJGH.mjs";
14
+ } from "./chunk-7YHCNO3N.mjs";
15
+ import "./chunk-SGL73IY5.mjs";
16
16
  import "./chunk-KZKGRND2.mjs";
17
- import "./chunk-BUKDWHTY.mjs";
17
+ import "./chunk-C46EE5BH.mjs";
18
18
 
19
19
  // ../dashboard/src/hooks/use-consolidation-log.ts
20
20
  import { useCallback as useCallback2, useMemo } from "react";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyxmate/memory",
3
- "version": "1.22.3",
3
+ "version": "1.23.0",
4
4
  "type": "module",
5
5
  "description": "SDK for pyx-memory — Memory as a Service for AI agents",
6
6
  "license": "MIT",