@pyxmate/memory 1.7.0 → 1.9.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 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 for tenant/namespace/ReBAC detail see the installed Persistent Memory rule.";
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).";
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-SVKIFOS5.mjs";
16
+ } from "./chunk-AXH45N7T.mjs";
17
17
  export {
18
18
  AGENT_TARGETS,
19
19
  PERSISTENT_MEMORY_SECTION,
@@ -1,5 +1,5 @@
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 for tenant/namespace/ReBAC detail see the installed Persistent Memory rule.`;
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).`;
3
3
  var PERSISTENT_MEMORY_SECTION = [
4
4
  "## Persistent Memory",
5
5
  "",
@@ -8,38 +8,38 @@ var PERSISTENT_MEMORY_SECTION = [
8
8
  "Search before assuming:",
9
9
  "",
10
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.",
11
+ "- Before suggesting an approach, search for prior decisions that might contradict it.",
12
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.',
13
+ "- When investigating a bug, search for prior occurrences and fixes.",
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, without excluding other results.',
15
+ "- Match search `effort` to risk: use `quick` for routine recall; use `deep` when quick returns nothing, when prior state may be archived/superseded, or when full history matters.",
16
+ '- To trace how a fact changed ("what did X use before Y", how it evolved), call `lineage` with `subject` + `relation` or an `entryId` instead of stitching together searches.',
17
+ "- Recalled memories reflect what was true when written \u2014 if one names a file, function, flag, or version, verify it still exists before acting on it.",
17
18
  "",
18
- "Reinforce what you use:",
19
+ "Reinforce and correct:",
19
20
  "",
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.",
21
+ "- After a retrieved memory actually informs your work (you cited it or acted on it), call `reinforce` on that memory. Recall keeps useful memories in the `quick`/`medium` tiers.",
22
+ "- When the user corrects a mistake, call `record_correction` with what was wrong, what to do instead, and when it applies.",
23
+ "- Before starting a task, call `fetch_applicable_corrections` with the task shape; pyx never auto-applies them, so decide which corrections fit.",
25
24
  "",
26
25
  "Store when durable state settles \u2014 usually before a final response or handoff. Treat that moment as a check, not an automatic write: sparse and high-value beats dense and noisy, so if nothing durable changed, store nothing. When it did, capture only what applies:",
27
26
  "",
28
- '- Facts (default) \u2014 corrections and preferences; bug fixes with root cause and fix; architecture or design decisions with reasoning; integration details (API format, auth flow, endpoint or tool behavior); gotchas with what broke and the fix; explicit "remember this" requests. One fact per memory.',
29
- "- Process (`type: long-term`) \u2014 a reusable workflow, convention, or playbook a fresh session should follow. Store it once it has stabilized or been adopted, not while you are still trying approaches.",
30
- "- Trajectory (`type: episodic`) \u2014 only at a real milestone, handoff, or user-requested checkpoint: one dated snapshot of what changed, where it now stands, and the open frontier. Not a running log \u2014 do not store step-by-step progress or ordinary end-of-turn summaries; for a maintained project or entity rollup call `summarize_memory_entity` instead.",
31
- "- Evolution \u2014 when a prior fact or decision changes, store the new state with its `eventTime` rather than overwriting the old one, so `lineage` can trace how and why it evolved. Route explicit user corrections through `record_correction`; keep durable user or project traits current with `upsert_user_profile`.",
27
+ '- Facts (default) \u2014 corrections/preferences; bug fixes with root cause and fix; architecture/design decisions with reasoning; integration/API/auth/endpoint details; gotchas with what broke and the fix; explicit "remember this" requests. One fact per memory.',
28
+ "- Process (`type: long-term`) \u2014 a reusable workflow, convention, or playbook a fresh session should follow. Store it once stabilized or adopted.",
29
+ "- 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.",
30
+ "- Evolution \u2014 when a prior fact or decision changes, store the new state with its `eventTime` rather than overwriting the old one, so `lineage` can trace how and why it evolved. Route explicit corrections through `record_correction`.",
32
31
  "",
33
32
  "Memory rules:",
34
33
  "",
35
34
  "- Always include `topic` and `project`.",
36
35
  "- Store concise facts and decisions, not deliberation.",
37
- "- Keep one memory per concept \u2014 do not bundle unrelated facts.",
38
- "- Do not store ephemeral file contents, raw logs, or step-by-step progress as it happens \u2014 a milestone or handoff snapshot (Trajectory, above) is the deliberate exception. The same applies to high-frequency streams (wearable/health samples, sensor readings, metrics): store the dated fact or summary they support (with `eventTime`), never one memory \u2014 or one graph node \u2014 per sample; the raw series belongs in the product database.",
39
- '- 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.',
40
- '- 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. Use specific durable entities ("sleep apnea", "morning run routine"); never a catch-all hub node ("me", "health", "work") linked to everything \u2014 a mega-hub degrades every traversal that touches it, and bounded `IS_A` category nodes (below) are the deliberate exception. 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.',
41
- "- Ingest documents/images only when they are worth persisting; images require a",
42
- " short description so they are searchable."
36
+ "- Keep one memory per concept; do not bundle unrelated facts.",
37
+ "- Do not store ephemeral file contents, raw logs, or step-by-step progress as it happens. High-frequency streams (wearable/health samples, sensor readings, metrics) must be aggregated into the dated fact or summary they support (with `eventTime`), never stored as one memory or graph node per sample.",
38
+ '- Pass `eventTime` (ISO-8601, when the fact happened or took effect) for facts 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 resolution key off it.',
39
+ "- Graph is a retrieval structure. When content names people, organizations, tools, places, events, or key concepts, pass `entities` and `relationships` when you can (caller-wins). On a multi-entity store, include at least one relationship connecting them, or use `triples` so edges are explicit. Relationships matter as much as entities for traversal. Entity-free memories are valid.",
40
+ '- Use specific durable entities ("sleep apnea", "morning run routine"). Never create a catch-all hub node ("me", "health", "work") linked to everything; mega-hubs degrade traversal. Bounded `IS_A` category nodes are the exception.',
41
+ '- For countable categories the user may later enumerate ("fitness classes", "streaming services", "pets"), add a canonical category `CONCEPT` node and an `IS_A` edge from each member to it (`"yoga"` IS_A `"fitness classes"`).',
42
+ "- Ingest documents/images only when they are worth persisting; images require a short description so they are searchable."
43
43
  ].join("\n");
44
44
  function buildDesignGuide({ appName }) {
45
45
  const name = appName.trim() || "your app";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  PERSISTENT_MEMORY_SECTION,
4
4
  buildDesignGuide
5
- } from "../chunk-SVKIFOS5.mjs";
5
+ } from "../chunk-AXH45N7T.mjs";
6
6
 
7
7
  // src/cli/exit-codes.ts
8
8
  var EXIT = {
@@ -798,7 +798,7 @@ function createProxyServer(client, version, uploadLocalFile) {
798
798
  return server;
799
799
  }
800
800
  async function runMcpProxyServer(opts) {
801
- const version = opts.version ?? (true ? "1.7.0" : "0.0.0-dev");
801
+ const version = opts.version ?? (true ? "1.9.0" : "0.0.0-dev");
802
802
  const read = await opts.readCredentials();
803
803
  if (!read.ok) {
804
804
  const text = read.result.content.map((c) => c.type === "text" ? c.text : "").join(" ").trim();
@@ -832,34 +832,9 @@ async function runMcpProxyServer(opts) {
832
832
  }
833
833
  }
834
834
 
835
- // src/cli/commands/mcp.ts
836
- async function mcpCommand() {
837
- const readCredentials = createReadCredentials(() => getDefaultKeychain());
838
- const onStdinEnd = new Promise((resolve3) => {
839
- process.stdin.once("end", resolve3);
840
- process.stdin.once("close", resolve3);
841
- });
842
- try {
843
- await Promise.race([runMcpProxyServer({ readCredentials }), onStdinEnd]);
844
- return EXIT.OK;
845
- } catch (err) {
846
- process.stderr.write(`Internal error: ${err instanceof Error ? err.message : String(err)}
847
- `);
848
- return EXIT.USAGE;
849
- }
850
- }
851
- function resolveMcpMode(flags) {
852
- if (flags.bundled === true) {
853
- return {
854
- ok: false,
855
- 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."
856
- };
857
- }
858
- return { ok: true };
859
- }
860
-
861
835
  // src/cli/commands/mcp-install.ts
862
836
  import { spawnSync as nodeSpawnSync } from "child_process";
837
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
863
838
  import { homedir } from "os";
864
839
  import { join } from "path";
865
840
 
@@ -1047,6 +1022,11 @@ function findLegacyPyxRange(lines, mask) {
1047
1022
  }
1048
1023
  return null;
1049
1024
  }
1025
+ function hasPyxMemoryRules(content) {
1026
+ const lines = content.replace(/\r\n/g, "\n").split("\n");
1027
+ const mask = fenceMask(lines);
1028
+ return findCompleteManagedRange(lines, mask) !== null || findOrphanBeginLine(lines, mask) !== -1 || findLegacyPyxRange(lines, mask) !== null;
1029
+ }
1050
1030
  function spliceBlock(lines, from, toExclusive, blockLines) {
1051
1031
  const before = lines.slice(0, from);
1052
1032
  const after = lines.slice(toExclusive);
@@ -1449,6 +1429,24 @@ function installMemoryRules(opts) {
1449
1429
  });
1450
1430
  process.stdout.write(result.message);
1451
1431
  }
1432
+ function reconcileMemoryRules(opts = {}) {
1433
+ const home = opts._homeDir ?? homedir();
1434
+ for (const segs of Object.values(RULES_FILE_TARGETS)) {
1435
+ const filePath = join(home, ...segs);
1436
+ try {
1437
+ if (!existsSync3(filePath)) continue;
1438
+ if (!hasPyxMemoryRules(readFileSync3(filePath, "utf8"))) continue;
1439
+ const result = writeMemoryRulesFile({ filePath, section: PERSISTENT_MEMORY_SECTION });
1440
+ if (result.action !== "unchanged") {
1441
+ process.stderr.write(
1442
+ `pyx-memory: refreshed the "## Persistent Memory" rules in ${filePath}
1443
+ `
1444
+ );
1445
+ }
1446
+ } catch {
1447
+ }
1448
+ }
1449
+ }
1452
1450
  function validateScope(scope) {
1453
1451
  if (VALID_SCOPES.has(scope)) return true;
1454
1452
  process.stderr.write(`Error: invalid --scope \`${scope}\`. Expected: local | user | project.
@@ -1476,8 +1474,35 @@ function writeJsonAndReport(filePath, agentLabel, entry, opts = {}) {
1476
1474
  return result.exitCode;
1477
1475
  }
1478
1476
 
1477
+ // src/cli/commands/mcp.ts
1478
+ async function mcpCommand() {
1479
+ reconcileMemoryRules();
1480
+ const readCredentials = createReadCredentials(() => getDefaultKeychain());
1481
+ const onStdinEnd = new Promise((resolve3) => {
1482
+ process.stdin.once("end", resolve3);
1483
+ process.stdin.once("close", resolve3);
1484
+ });
1485
+ try {
1486
+ await Promise.race([runMcpProxyServer({ readCredentials }), onStdinEnd]);
1487
+ return EXIT.OK;
1488
+ } catch (err) {
1489
+ process.stderr.write(`Internal error: ${err instanceof Error ? err.message : String(err)}
1490
+ `);
1491
+ return EXIT.USAGE;
1492
+ }
1493
+ }
1494
+ function resolveMcpMode(flags) {
1495
+ if (flags.bundled === true) {
1496
+ return {
1497
+ ok: false,
1498
+ 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."
1499
+ };
1500
+ }
1501
+ return { ok: true };
1502
+ }
1503
+
1479
1504
  // src/cli/commands/scaffold.ts
1480
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
1505
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
1481
1506
  import { basename as basename2, join as join2, resolve as resolve2 } from "path";
1482
1507
  var SERVER_IMAGE = "ghcr.io/pyx-corp/pyx-memory-v1:latest";
1483
1508
  var DOCKER_COMPOSE = `services:
@@ -1619,7 +1644,7 @@ function buildFiles(targetDir, appName) {
1619
1644
  function scaffoldCommand(args = {}) {
1620
1645
  const target = resolveTarget(args);
1621
1646
  if (target === null) return EXIT.USAGE;
1622
- if (existsSync3(target.targetDir) && !statSync2(target.targetDir).isDirectory()) {
1647
+ if (existsSync4(target.targetDir) && !statSync2(target.targetDir).isDirectory()) {
1623
1648
  process.stderr.write(`Error: target exists and is not a directory: ${target.targetDir}
1624
1649
  `);
1625
1650
  return EXIT.USAGE;
@@ -1629,7 +1654,7 @@ function scaffoldCommand(args = {}) {
1629
1654
  const skipped = [];
1630
1655
  for (const file of buildFiles(target.targetDir, target.appName)) {
1631
1656
  const relativePath = basename2(file.path);
1632
- if (existsSync3(file.path)) {
1657
+ if (existsSync4(file.path)) {
1633
1658
  skipped.push(relativePath);
1634
1659
  continue;
1635
1660
  }
package/dist/index.d.ts CHANGED
@@ -1009,6 +1009,21 @@ interface CorrectionRecord {
1009
1009
  /** Overlap-coefficient score between the query `taskShape` and this row's `appliesWhen`. */
1010
1010
  score: number;
1011
1011
  }
1012
+ /**
1013
+ * Rolling graph-traversal telemetry (in-process, since boot). Watches the
1014
+ * engine re-review triggers: depth-2 getNeighbors p95 latency / result size
1015
+ * (hub explosion) and getAllEdges job duration.
1016
+ */
1017
+ interface GraphTelemetrySnapshot {
1018
+ /** p95 latency (ms) of getNeighbors calls with depth >= 2; null until sampled. */
1019
+ neighborsDepth2P95Ms: number | null;
1020
+ /** p95 result node count of getNeighbors calls with depth >= 2; null until sampled. */
1021
+ neighborsDepth2ResultP95: number | null;
1022
+ /** Depth >= 2 samples in the rolling window (confidence for the p95s). */
1023
+ neighborsDepth2SampleCount: number;
1024
+ /** Duration (ms) of the most recent getAllEdges call; null until one ran. */
1025
+ allEdgesLastMs: number | null;
1026
+ }
1012
1027
  interface MemoryStats {
1013
1028
  totalEntries: number;
1014
1029
  storageUsedBytes: number;
@@ -1022,6 +1037,8 @@ interface MemoryStats {
1022
1037
  graphRawEdgeCount?: number;
1023
1038
  /** Name of the active graph store ('neo4j', 'sqlite', or undefined if none). */
1024
1039
  graphStore?: string;
1040
+ /** Graph traversal telemetry; undefined when no graph store is active. */
1041
+ graphTelemetry?: GraphTelemetrySnapshot;
1025
1042
  /** Whether the memory service is connected. False when using DisabledMemory. */
1026
1043
  connected?: boolean;
1027
1044
  }
@@ -1465,4 +1482,4 @@ interface CreatePyxMemoryOptions {
1465
1482
  }
1466
1483
  declare function createPyxMemory(opts?: CreatePyxMemoryOptions): MemoryClient;
1467
1484
 
1468
- export { type AgentId, type ApiResponse, type ConsolidationRunResult, type CorrectionInput, type CorrectionRecord, type CreatePyxMemoryOptions, DEFAULTS, DEPRECATED_RAG_STRATEGIES, DisabledMemory, type DroppedGraphRelationship, EmbeddingProviderName, type EnrichmentCallbacks, type EntityExtractionResult, type ExtendedMemoryInterface, type FetchCorrectionsInput, type GraphEnrichment, type GraphEnrichmentStatus, type GraphFailureMode, type GraphNode, type GraphRelationship, type GraphRepairResult, type GraphTraversalResult, type IngestEntity, type IngestErrorEvent, type IngestEvent, type IngestFileOptions, type IngestHeartbeatEvent, type IngestProgressEvent, type IngestRelationship, type IngestResultEvent, type IngestStage, type IngestionResult, type LineageParams, type LineageResult, type LineageVersion, MemoryClient, type MemoryClientOptions, type MemoryEntry, type MemoryIngestRequest, type MemoryInterface, type MemoryListParams, type MemoryListResult, type MemoryLogFilters, type MemorySearchParams, type MemorySearchResult, MemoryServerError, type MemoryStats, MemoryType, type MoveEntriesFilter, MoveFailureReason, type MoveResult, type MoveTarget, NamespaceIsolation, type PrincipalContext, RAGStrategy, type ReinforceParams, type ReinforceResult, type ReinforceSignal, SINGLE_TENANT_ID, SensitivityLevel, type SourceEvidence, type StoreInput, StoreTarget, type TemporalQueryFilters, type TenantScopeOptions, type Timestamp, type Topology, type TopologyExtractionProvider, type TopologyServiceVariant, VectorProvider, type VectorStatus, type WikiLintReport, createPyxMemory, mergeExtractedEntities, normalizeGraphLabel, normalizeNameKey };
1485
+ export { type AgentId, type ApiResponse, type ConsolidationRunResult, type CorrectionInput, type CorrectionRecord, type CreatePyxMemoryOptions, DEFAULTS, DEPRECATED_RAG_STRATEGIES, DisabledMemory, type DroppedGraphRelationship, EmbeddingProviderName, type EnrichmentCallbacks, type EntityExtractionResult, type ExtendedMemoryInterface, type FetchCorrectionsInput, type GraphEnrichment, type GraphEnrichmentStatus, type GraphFailureMode, type GraphNode, type GraphRelationship, type GraphRepairResult, type GraphTelemetrySnapshot, type GraphTraversalResult, type IngestEntity, type IngestErrorEvent, type IngestEvent, type IngestFileOptions, type IngestHeartbeatEvent, type IngestProgressEvent, type IngestRelationship, type IngestResultEvent, type IngestStage, type IngestionResult, type LineageParams, type LineageResult, type LineageVersion, MemoryClient, type MemoryClientOptions, type MemoryEntry, type MemoryIngestRequest, type MemoryInterface, type MemoryListParams, type MemoryListResult, type MemoryLogFilters, type MemorySearchParams, type MemorySearchResult, MemoryServerError, type MemoryStats, MemoryType, type MoveEntriesFilter, MoveFailureReason, type MoveResult, type MoveTarget, NamespaceIsolation, type PrincipalContext, RAGStrategy, type ReinforceParams, type ReinforceResult, type ReinforceSignal, SINGLE_TENANT_ID, SensitivityLevel, type SourceEvidence, type StoreInput, StoreTarget, type TemporalQueryFilters, type TenantScopeOptions, type Timestamp, type Topology, type TopologyExtractionProvider, type TopologyServiceVariant, VectorProvider, type VectorStatus, type WikiLintReport, createPyxMemory, mergeExtractedEntities, normalizeGraphLabel, normalizeNameKey };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyxmate/memory",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "type": "module",
5
5
  "description": "SDK for pyx-memory — Memory as a Service for AI agents",
6
6
  "license": "MIT",