@pyxmate/memory 0.24.1 → 0.26.1

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 CHANGED
@@ -36,6 +36,15 @@ Restart Claude Code. The 7 memory tools (`search_memories`, `store_memory`,
36
36
  `get_memory`, `list_memories`, `delete_memory`, `ingest_memory_file`,
37
37
  `summarize_memory_entity`) are auto-discovered via MCP Tool Search.
38
38
 
39
+ **That's the whole setup** — no extra LLM API keys. When the agent calls
40
+ `store_memory` without supplying `entities`/`relationships`, the MCP tool
41
+ auto-extracts a graph topology by asking the agent's own LLM via MCP sampling
42
+ (same pattern as image-description / `extractEntitiesV2` for file ingest).
43
+ Your LLM, your credentials. Pass `extractEntities: false` on a per-call basis
44
+ to opt out of auto-extraction. See
45
+ [`graph-auto-entity-extraction-v2`](https://github.com/pyx-corp/pyx-memory-v1/blob/main/docs/specs/graph-auto-entity-extraction-v2/spec.md)
46
+ for the full contract.
47
+
39
48
  Drop the [agent-template snippet](https://github.com/pyx-corp/pyx-memory-v1/blob/main/docs/agent-template.md)
40
49
  into your project's `CLAUDE.md` / `AGENTS.md` to tell the agent WHEN to search
41
50
  vs store.
@@ -59,6 +68,36 @@ await memory.store({
59
68
  const results = await memory.search({ query: 'deadline', limit: 5 });
60
69
  ```
61
70
 
71
+ ### Optional: client-side auto-extraction
72
+
73
+ `MemoryClient.store(entry, options)` accepts an `enrichment.extractEntities`
74
+ callback. The SDK invokes your callback (running against **your own** LLM
75
+ credentials), merges the result with any caller-supplied entities using
76
+ case-insensitive caller-wins, and sends the populated graph data to the server.
77
+ Mirrors the existing file-ingest `extractEntitiesV2` pattern.
78
+
79
+ ```ts
80
+ await memory.store(
81
+ { content: 'Andrej Karpathy joined OpenAI as a co-founder in late 2015.',
82
+ metadata: { topic: 'history', project: 'orca' } },
83
+ {
84
+ enrichment: {
85
+ extractEntities: async ({ content, signal }) => {
86
+ // Call YOUR LLM here. Return { entities, relations } matching the
87
+ // EntityType/RelationType vocabularies. Throw on parse failures —
88
+ // errors propagate to the caller, no silent swallow.
89
+ return { entities: [...], relations: [...] };
90
+ },
91
+ },
92
+ signal, // AbortSignal forwarded into both the callback and the fetch
93
+ },
94
+ );
95
+ ```
96
+
97
+ Per-call `entry.extractEntities: false` skips the callback entirely;
98
+ `entry.extractEntities: true` with no callback throws a loud error (no silent
99
+ no-op). The server never sees an LLM API key — it just persists what you send.
100
+
62
101
  ## Entry Points
63
102
 
64
103
  | Import | What you get |
@@ -0,0 +1,118 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // ../shared/src/constants/defaults.ts
8
+ var DEFAULTS = {
9
+ DATA_DIR: "./data",
10
+ VECTOR_PROVIDER: "lancedb",
11
+ MEMORY_SERVER_PORT: 7822
12
+ };
13
+
14
+ // ../shared/src/graph/extraction.ts
15
+ function mergeExtractedEntities(callerEntities, callerRelationships, extracted) {
16
+ const entities = [...callerEntities ?? []];
17
+ const relationships = [...callerRelationships ?? []];
18
+ const nameByLowercase = /* @__PURE__ */ new Map();
19
+ for (const entity of entities) {
20
+ const key = entity.name.toLowerCase();
21
+ if (!nameByLowercase.has(key)) nameByLowercase.set(key, entity.name);
22
+ }
23
+ for (const entity of extracted.entities) {
24
+ const key = entity.name.toLowerCase();
25
+ if (nameByLowercase.has(key)) continue;
26
+ entities.push(entity);
27
+ nameByLowercase.set(key, entity.name);
28
+ }
29
+ for (const relationship of extracted.relations) {
30
+ const source = nameByLowercase.get(relationship.source.toLowerCase());
31
+ const target = nameByLowercase.get(relationship.target.toLowerCase());
32
+ if (source && target) {
33
+ relationships.push({ ...relationship, source, target });
34
+ }
35
+ }
36
+ return { entities, relationships };
37
+ }
38
+
39
+ // ../shared/src/types/isolation.ts
40
+ var NamespaceIsolation = {
41
+ SHARED: "shared",
42
+ STRICT: "strict"
43
+ };
44
+
45
+ // ../shared/src/types/memory.ts
46
+ var MemoryType = {
47
+ SHORT_TERM: "short-term",
48
+ LONG_TERM: "long-term",
49
+ WORKING: "working",
50
+ EPISODIC: "episodic",
51
+ SUMMARY: "summary"
52
+ };
53
+ var SensitivityLevel = {
54
+ PUBLIC: "public",
55
+ INTERNAL: "internal",
56
+ SECRET: "secret"
57
+ };
58
+ var RAGStrategy = {
59
+ NAIVE: "naive",
60
+ GRAPH: "graph",
61
+ AGENTIC: "agentic",
62
+ HYBRID: "hybrid"
63
+ };
64
+ var VectorProvider = {
65
+ LANCEDB: "lancedb"
66
+ };
67
+ var EmbeddingProviderName = {
68
+ STUB: "stub",
69
+ /** @deprecated Vestigial — pyx-memory uses internal EmbeddingGemma embeddings. */
70
+ ANTHROPIC: "anthropic",
71
+ /** @deprecated Vestigial — pyx-memory uses internal EmbeddingGemma embeddings. */
72
+ OPENAI: "openai",
73
+ /** In-process ONNX model (default: EmbeddingGemma-300M). */
74
+ LOCAL: "local",
75
+ /** Remote OpenAI-compatible embedding service (pyx-cloud shared, custom, etc.). */
76
+ HTTP: "http"
77
+ };
78
+ var StoreTarget = {
79
+ SQLITE: "sqlite",
80
+ VECTOR: "vector",
81
+ GRAPH: "graph"
82
+ };
83
+
84
+ // ../shared/src/types/move.ts
85
+ var MoveFailureReason = {
86
+ /** Entry not found in the caller's tenant. */
87
+ NOT_FOUND: "not_found",
88
+ /** Move would cross tenant boundary (always forbidden). */
89
+ CROSS_TENANT_FORBIDDEN: "cross_tenant_forbidden",
90
+ /** Target namespace ID does not exist in the caller's tenant. */
91
+ TARGET_NAMESPACE_NOT_FOUND: "target_namespace_not_found",
92
+ /** SQLite metadata update failed; no compensation needed. */
93
+ SQLITE_UPDATE_FAILED: "sqlite_update_failed",
94
+ /** Vector store metadata update failed; SQLite reverted. */
95
+ VECTOR_UPDATE_FAILED: "vector_update_failed",
96
+ /** Graph edge namespace update failed; SQLite + vector reverted. */
97
+ GRAPH_UPDATE_FAILED: "graph_update_failed",
98
+ /** Compensation itself failed — manual intervention required. */
99
+ COMPENSATION_FAILED: "compensation_failed"
100
+ };
101
+
102
+ // ../shared/src/types/principal.ts
103
+ var SINGLE_TENANT_ID = "_single";
104
+
105
+ export {
106
+ __export,
107
+ DEFAULTS,
108
+ mergeExtractedEntities,
109
+ NamespaceIsolation,
110
+ MemoryType,
111
+ SensitivityLevel,
112
+ RAGStrategy,
113
+ VectorProvider,
114
+ EmbeddingProviderName,
115
+ StoreTarget,
116
+ MoveFailureReason,
117
+ SINGLE_TENANT_ID
118
+ };
@@ -1,3 +1,7 @@
1
+ import {
2
+ mergeExtractedEntities
3
+ } from "./chunk-573W637Y.mjs";
4
+
1
5
  // ../client/src/memory-client.ts
2
6
  var MemoryServerError = class extends Error {
3
7
  status;
@@ -50,10 +54,34 @@ var MemoryClient = class {
50
54
  throw new Error(`Memory server not reachable at ${this.baseUrl}: ${response.status}`);
51
55
  }
52
56
  }
53
- async store(entry) {
57
+ async store(entry, options) {
58
+ const callback = options?.enrichment?.extractEntities;
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
+ let payload = entry;
67
+ if (callback && !optedOut) {
68
+ const extracted = await callback({
69
+ content: entry.content,
70
+ ...entry.metadata ? { metadata: entry.metadata } : {},
71
+ ...options?.signal ? { signal: options.signal } : {}
72
+ });
73
+ const merged = mergeExtractedEntities(entry.entities, entry.relationships, extracted);
74
+ const { extractEntities: _drop, ...rest } = entry;
75
+ payload = {
76
+ ...rest,
77
+ entities: merged.entities,
78
+ relationships: merged.relationships
79
+ };
80
+ }
54
81
  return this.fetchApi("/api/memory/ingest", {
55
82
  method: "POST",
56
- body: JSON.stringify(entry)
83
+ body: JSON.stringify(payload),
84
+ ...options?.signal ? { signal: options.signal } : {}
57
85
  });
58
86
  }
59
87
  async search(params) {
@@ -121,6 +149,9 @@ var MemoryClient = class {
121
149
  if (params.limit != null) searchParams.set("limit", String(params.limit));
122
150
  if (params.type) searchParams.set("type", params.type);
123
151
  if (params.agentId) searchParams.set("agentId", params.agentId);
152
+ if (params.includeKinds && params.includeKinds.length > 0) {
153
+ searchParams.set("includeKinds", params.includeKinds.join(","));
154
+ }
124
155
  const qs = searchParams.toString();
125
156
  return this.fetchApi(`/api/memory/entries${qs ? `?${qs}` : ""}`);
126
157
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  MemoryClient
3
- } from "./chunk-CCGW6QRX.mjs";
3
+ } from "./chunk-AKQIZAFT.mjs";
4
4
 
5
5
  // ../dashboard/src/aggregations/consolidation-analytics.ts
6
6
  function analyzeConsolidationLog(entries) {
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- __export
4
- } from "../chunk-7P6ASYW6.mjs";
3
+ __export,
4
+ mergeExtractedEntities
5
+ } from "../chunk-573W637Y.mjs";
5
6
 
6
7
  // src/cli/exit-codes.ts
7
8
  var EXIT = {
@@ -517,6 +518,44 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
517
518
  // src/mcp/instructions.ts
518
519
  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. Add entities/relationships for named people, tools, organizations, locations, or events. Use file ingest for documents/images worth persisting; images require a description.`;
519
520
 
521
+ // src/mcp/sampling.ts
522
+ function createSamplingClient(server) {
523
+ function isAvailable() {
524
+ const caps = server.server.getClientCapabilities();
525
+ return Boolean(caps?.sampling);
526
+ }
527
+ return {
528
+ isAvailable,
529
+ async complete(prompt, opts) {
530
+ if (!isAvailable()) {
531
+ throw new Error(
532
+ "MCP sampling unavailable: the connected client did not advertise the `sampling` capability. Either supply entities/relationships explicitly or set extractEntities:false on the request."
533
+ );
534
+ }
535
+ const result = await server.server.createMessage(
536
+ {
537
+ messages: [{ role: "user", content: { type: "text", text: prompt } }],
538
+ maxTokens: opts?.maxTokens ?? 2048
539
+ },
540
+ opts?.signal ? { signal: opts.signal } : void 0
541
+ );
542
+ const content = result.content;
543
+ if (content && typeof content === "object" && !Array.isArray(content)) {
544
+ const block = content;
545
+ if (block.type === "text" && typeof block.text === "string") return block.text;
546
+ }
547
+ if (Array.isArray(content)) {
548
+ for (const block of content) {
549
+ if (block && typeof block === "object" && block.type === "text" && typeof block.text === "string") {
550
+ return block.text;
551
+ }
552
+ }
553
+ }
554
+ throw new Error("MCP sampling returned no text content");
555
+ }
556
+ };
557
+ }
558
+
520
559
  // ../../node_modules/zod/v4/classic/external.js
521
560
  var external_exports = {};
522
561
  __export(external_exports, {
@@ -15362,6 +15401,61 @@ var statusTool = {
15362
15401
  }
15363
15402
  };
15364
15403
 
15404
+ // src/mcp/extraction-prompt.ts
15405
+ var ENTITY_TYPES = ["PERSON", "ORGANIZATION", "CONCEPT", "TOOL", "LOCATION", "EVENT"];
15406
+ var RELATION_TYPES = [
15407
+ "USES",
15408
+ "OWNS",
15409
+ "DEPENDS_ON",
15410
+ "RELATED_TO",
15411
+ "CREATED_BY",
15412
+ "PART_OF",
15413
+ "IS_A",
15414
+ "WORKS_AT",
15415
+ "LOCATED_IN"
15416
+ ];
15417
+ var ExtractionSchema = external_exports.object({
15418
+ entities: external_exports.array(
15419
+ external_exports.object({
15420
+ name: external_exports.string().min(1),
15421
+ type: external_exports.enum(ENTITY_TYPES)
15422
+ })
15423
+ ),
15424
+ relations: external_exports.array(
15425
+ external_exports.object({
15426
+ source: external_exports.string().min(1),
15427
+ target: external_exports.string().min(1),
15428
+ type: external_exports.enum(RELATION_TYPES)
15429
+ })
15430
+ )
15431
+ });
15432
+ function buildExtractionPrompt(content) {
15433
+ return [
15434
+ "Extract graph facts as JSON only. No prose, no fences, no commentary.",
15435
+ `Schema: {"entities":[{"name":string,"type":EntityType}],"relations":[{"source":string,"target":string,"type":RelationType}]}.`,
15436
+ `EntityType values: ${ENTITY_TYPES.join(", ")}.`,
15437
+ `RelationType values: ${RELATION_TYPES.join(", ")}.`,
15438
+ "Include only entities/relations explicitly named or strongly implied in the content. Empty arrays are valid.",
15439
+ `Content: ${content}`
15440
+ ].join("\n");
15441
+ }
15442
+ function parseExtractionResponse(raw) {
15443
+ let parsed;
15444
+ try {
15445
+ parsed = JSON.parse(raw);
15446
+ } catch (cause) {
15447
+ throw new Error(
15448
+ `MCP sampling returned non-JSON text (first 120 chars: ${raw.slice(0, 120).replace(/\n/g, "\\n")})`,
15449
+ { cause: cause instanceof Error ? cause : void 0 }
15450
+ );
15451
+ }
15452
+ const result = ExtractionSchema.safeParse(parsed);
15453
+ if (!result.success) {
15454
+ throw new Error(`MCP sampling response failed schema validation: ${result.error.message}`);
15455
+ }
15456
+ return result.data;
15457
+ }
15458
+
15365
15459
  // src/mcp/tools/store.ts
15366
15460
  var entityTypes = ["PERSON", "ORGANIZATION", "CONCEPT", "TOOL", "LOCATION", "EVENT"];
15367
15461
  var relationshipTypes = [
@@ -15391,29 +15485,57 @@ var inputShape7 = {
15391
15485
  name: external_exports.string().min(1).describe("Entity name as referenced in content."),
15392
15486
  type: external_exports.enum(entityTypes).describe("Entity type.")
15393
15487
  })
15394
- ).optional().describe("Named entities mentioned by the content; required when relationships are present."),
15488
+ ).optional().describe(
15489
+ "Named entities mentioned by the content; required when relationships are present. When omitted, the MCP tool may auto-extract via the connected client's LLM (MCP sampling) before sending to the server; set `extractEntities:false` to skip."
15490
+ ),
15395
15491
  relationships: external_exports.array(
15396
15492
  external_exports.object({
15397
15493
  source: external_exports.string().min(1).describe("Source entity name (must appear in entities array)."),
15398
15494
  target: external_exports.string().min(1).describe("Target entity name (must appear in entities array)."),
15399
15495
  type: external_exports.enum(relationshipTypes).describe("Relationship type.")
15400
15496
  })
15401
- ).optional().describe("Edges between entities; source and target must be entity names from this request."),
15497
+ ).optional().describe(
15498
+ "Edges between entities; source and target must be entity names from this request. When omitted, the MCP tool may auto-extract via the connected client's LLM (MCP sampling) before sending to the server; set `extractEntities:false` to skip."
15499
+ ),
15500
+ extractEntities: external_exports.boolean().optional().describe(
15501
+ "Override client-side auto-extraction (MCP sampling): false to skip, true to require (errors if the connected client does not support sampling)."
15502
+ ),
15402
15503
  ...scopeShape
15403
15504
  };
15404
15505
  var storeMemoryTool = {
15405
15506
  name: "store_memory",
15406
15507
  config: {
15407
15508
  title: "Store pyx-memory entry",
15408
- description: "Store one concise factual memory with required topic and project metadata. Provide entities/relationships when content names people, tools, orgs, locations, or events.",
15509
+ description: "Store one concise factual memory with required topic and project metadata. Provide entities/relationships explicitly when you want exact graph topology; when omitted, the MCP tool may auto-extract using your own LLM via MCP sampling before sending to the server.",
15409
15510
  inputSchema: inputShape7,
15410
15511
  annotations: { readOnlyHint: false, idempotentHint: false, openWorldHint: true }
15411
15512
  },
15412
- handler: (deps) => async (raw) => {
15513
+ handler: (deps) => async (raw, ctx) => {
15413
15514
  const args = raw;
15414
15515
  const creds = await deps.readCredentials();
15415
15516
  if (!creds.ok) return creds.result;
15416
15517
  const http = createHttpClient(creds.credentials, deps.fetchImpl);
15518
+ let entities = args.entities;
15519
+ let relationships = args.relationships;
15520
+ const samplingAvailable = deps.samplingClient?.isAvailable() ?? false;
15521
+ const optedOut = args.extractEntities === false;
15522
+ const forced = args.extractEntities === true;
15523
+ if (forced && !samplingAvailable) {
15524
+ throw new Error(
15525
+ "extractEntities=true requested but the connected MCP client did not advertise the `sampling` capability. Pass entities/relationships explicitly, set extractEntities:false, or connect a sampling-capable client."
15526
+ );
15527
+ }
15528
+ if (deps.samplingClient && samplingAvailable && !optedOut && (entities?.length ?? 0) === 0) {
15529
+ const prompt = buildExtractionPrompt(args.content);
15530
+ const completion = await deps.samplingClient.complete(
15531
+ prompt,
15532
+ ctx?.signal ? { signal: ctx.signal } : void 0
15533
+ );
15534
+ const extracted = parseExtractionResponse(completion);
15535
+ const merged = mergeExtractedEntities(args.entities, args.relationships, extracted);
15536
+ entities = merged.entities;
15537
+ relationships = merged.relationships;
15538
+ }
15417
15539
  const body = {
15418
15540
  content: args.content,
15419
15541
  type: args.type ?? "long-term",
@@ -15427,8 +15549,8 @@ var storeMemoryTool = {
15427
15549
  agentId: args.agentId,
15428
15550
  sessionId: args.sessionId,
15429
15551
  parentId: args.parentId,
15430
- entities: args.entities,
15431
- relationships: args.relationships
15552
+ entities,
15553
+ relationships
15432
15554
  };
15433
15555
  const res = await http.requestJson({
15434
15556
  method: "POST",
@@ -15499,17 +15621,22 @@ var ALL_TOOL_NAMES = ALL_TOOLS.map((t) => t.name);
15499
15621
  // src/mcp/server.ts
15500
15622
  async function runMcpServer(opts) {
15501
15623
  const fetchImpl = opts.fetchImpl ?? fetch;
15502
- const version2 = opts.version ?? (true ? "0.24.1" : "0.0.0-dev");
15624
+ const version2 = opts.version ?? (true ? "0.26.1" : "0.0.0-dev");
15503
15625
  const server = new McpServer(
15504
15626
  { name: "pyx-memory", version: version2 },
15505
15627
  { instructions: PYX_MEMORY_INSTRUCTIONS, capabilities: { tools: {} } }
15506
15628
  );
15629
+ const samplingClient = createSamplingClient(server);
15507
15630
  for (const tool of ALL_TOOLS) {
15508
- const handle = tool.handler({ readCredentials: opts.readCredentials, fetchImpl });
15631
+ const handle = tool.handler({
15632
+ readCredentials: opts.readCredentials,
15633
+ fetchImpl,
15634
+ samplingClient
15635
+ });
15509
15636
  server.registerTool(
15510
15637
  tool.name,
15511
15638
  tool.config,
15512
- async (args) => handle(args)
15639
+ async (args, extra) => handle(args, { signal: extra?.signal })
15513
15640
  );
15514
15641
  }
15515
15642
  const transport = new StdioServerTransport();
@@ -15681,6 +15808,7 @@ function mcpInstallClaudeCodeCommand(opts = {}) {
15681
15808
  process.stdout.write(
15682
15809
  `Installed pyx-memory MCP server in Claude Code (scope: ${scope}).
15683
15810
  Restart Claude Code to make the tools available. No API key was written to .mcp.json \u2014 credentials live in the OS credential store.
15811
+ Graph entities/relationships auto-extract via your client's own LLM (MCP sampling) when you call store_memory without entities \u2014 no extra LLM keys to configure.
15684
15812
  `
15685
15813
  );
15686
15814
  return EXIT.OK;
@@ -11,9 +11,9 @@ import {
11
11
  toGraphologyFormat,
12
12
  transformGraphData,
13
13
  unreachableHealth
14
- } from "./chunk-FSFVD3MB.mjs";
15
- import "./chunk-CCGW6QRX.mjs";
16
- import "./chunk-7P6ASYW6.mjs";
14
+ } from "./chunk-CE3ZBDTM.mjs";
15
+ import "./chunk-AKQIZAFT.mjs";
16
+ import "./chunk-573W637Y.mjs";
17
17
  export {
18
18
  DashboardClient,
19
19
  Poller,
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
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, MemoryStats as MemoryStats$1, WikiLintReport as WikiLintReport$1, ExtractedImageMeta as ExtractedImageMeta$1, IngestEntity as IngestEntity$1, IngestRelationship as IngestRelationship$1, Topology as Topology$1, IngestEvent as IngestEvent$1, GraphNode as GraphNode$1, GraphTraversalResult as GraphTraversalResult$1 } from '@pyx-memory/shared';
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, MemoryStats as MemoryStats$1, WikiLintReport as WikiLintReport$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, GraphNode as GraphNode$1, GraphTraversalResult as GraphTraversalResult$1 } from '@pyx-memory/shared';
2
2
 
3
3
  /** Parameters for paginated entry listing. */
4
4
  interface MemoryListParams {
@@ -19,6 +19,15 @@ interface MemoryListParams {
19
19
  * and get the legacy tenant-only scope.
20
20
  */
21
21
  principal?: PrincipalContext$1;
22
+ /**
23
+ * R1/R2 — opt-in list of `metadata._kind` values to include in the
24
+ * response. When omitted or empty, the default-set returns only entries
25
+ * with NO `_kind` system metadata (e.g. `_kind:'entity-summary'` rows
26
+ * are hidden). When supplied, the listed kinds are returned IN ADDITION
27
+ * to the default-set (additive, not replacement). See
28
+ * `docs/specs/topic-org-v025-f1-include-kinds/spec.md`.
29
+ */
30
+ includeKinds?: string[];
22
31
  }
23
32
  /** Result of a paginated entry listing. */
24
33
  interface MemoryListResult {
@@ -169,6 +178,35 @@ interface IngestFileOptions {
169
178
  signal?: AbortSignal;
170
179
  namespaceId?: string;
171
180
  }
181
+ /**
182
+ * Caller-supplied enrichment for the per-call store path. Mirrors
183
+ * {@link EnrichmentCallbacks} for file ingest — the SDK invokes the callback
184
+ * (against the caller's own LLM credentials) and merges the result into the
185
+ * ingest payload before POSTing, so the server holds no LLM keys.
186
+ */
187
+ interface StoreEnrichmentCallbacks {
188
+ /**
189
+ * Extract entities + relationships from `content`. The caller's LLM does
190
+ * the work; the SDK passes the result through {@link mergeExtractedEntities}
191
+ * (caller-wins, case-insensitive) before sending to the server.
192
+ *
193
+ * Skipped when `entry.extractEntities === false`. When
194
+ * `entry.extractEntities === true` but this callback is not supplied,
195
+ * `MemoryClient.store()` throws a loud error rather than silently no-op'ing
196
+ * (same shape as the in-process Memory.store() guard).
197
+ */
198
+ extractEntities?: (input: {
199
+ content: string;
200
+ metadata?: Record<string, unknown>;
201
+ signal?: AbortSignal;
202
+ }) => Promise<EntityExtractionResult$1>;
203
+ }
204
+ /** Options accepted by {@link MemoryClient.store}. */
205
+ interface StoreOptions {
206
+ enrichment?: StoreEnrichmentCallbacks;
207
+ /** Propagated into the `extractEntities` callback and the underlying fetch. */
208
+ signal?: AbortSignal;
209
+ }
172
210
  /** Error thrown by MemoryClient when the server returns a non-success response. */
173
211
  declare class MemoryServerError extends Error {
174
212
  readonly status: number;
@@ -205,7 +243,7 @@ declare class MemoryClient implements ExtendedMemoryInterface {
205
243
  /** Encode a path segment to prevent URL injection */
206
244
  private encodePathSegment;
207
245
  initialize(): Promise<void>;
208
- store(entry: StoreInput$1): Promise<MemoryEntry$1>;
246
+ store(entry: StoreInput$1, options?: StoreOptions): Promise<MemoryEntry$1>;
209
247
  search(params: MemorySearchParams$1): Promise<MemorySearchResult$1>;
210
248
  get(id: string): Promise<MemoryEntry$1 | null>;
211
249
  delete(id: string): Promise<boolean>;
@@ -328,51 +366,6 @@ declare const DEFAULTS: {
328
366
  readonly MEMORY_SERVER_PORT: 7822;
329
367
  };
330
368
 
331
- interface ApiResponse<T> {
332
- success: boolean;
333
- data?: T;
334
- error?: string;
335
- }
336
- /**
337
- * Build variant of the running pyx-memory image. Detected from the actual
338
- * availability of `@huggingface/transformers` at boot — `full` keeps the
339
- * local-embedding stack bundled, `cloud` has it pruned (EMBEDDING_PROVIDER=http
340
- * is mandatory). The detected value is authoritative; the optional
341
- * `PYX_BUILD_VARIANT` env stamp is a sanity check that throws on drift.
342
- */
343
- type TopologyServiceVariant = 'cloud' | 'full';
344
- /**
345
- * Where embedding work happens. `inline` means the server computes vectors
346
- * in-process (EMBEDDING_PROVIDER=local); `remote` means it delegates to an
347
- * HTTP embedding service (EMBEDDING_PROVIDER=http). The endpoint URL itself
348
- * is internal infrastructure and is intentionally NOT exposed here.
349
- */
350
- type TopologyEmbeddingLocation = 'inline' | 'remote';
351
- /**
352
- * Public topology snapshot returned by `GET /status`. Reflects actual runtime
353
- * capability (variant detected from import probe, embedding location derived
354
- * from configured provider) plus the active embedding profile. Never leaks
355
- * embedding-service URLs or other infrastructure host fragments — those stay
356
- * behind the admin surface.
357
- */
358
- interface Topology {
359
- service: {
360
- variant: TopologyServiceVariant;
361
- /**
362
- * Operator-declared role label (`PYX_DEPLOYMENT_ROLE`). Free-form string.
363
- * Structurally absent from the response when the env var is unset or empty.
364
- */
365
- declaredRole?: string;
366
- version: string;
367
- };
368
- embedding: {
369
- location: TopologyEmbeddingLocation;
370
- modelKey: string;
371
- modelId: string;
372
- dimensions: number;
373
- };
374
- }
375
-
376
369
  /** ISO 8601 timestamp string */
377
370
  type Timestamp = string;
378
371
  /** Unique agent identifier */
@@ -587,6 +580,8 @@ type StoreInput = Omit<MemoryEntry, 'id' | 'createdAt'> & {
587
580
  entities?: IngestEntity[];
588
581
  /** Agent-provided relationships for graph storage. */
589
582
  relationships?: IngestRelationship[];
583
+ /** Override server-side auto-extraction for this store call. */
584
+ extractEntities?: boolean;
590
585
  /** Graph-failure handling. Default: "throw" (loud) — see GraphFailureMode. */
591
586
  graphFailureMode?: GraphFailureMode;
592
587
  };
@@ -602,6 +597,8 @@ interface MemoryIngestRequest {
602
597
  entities?: IngestEntity[];
603
598
  /** Agent-provided relationships for graph storage. */
604
599
  relationships?: IngestRelationship[];
600
+ /** Override server-side auto-extraction for this ingest request. */
601
+ extractEntities?: boolean;
605
602
  /** Graph-failure handling. Default: "throw" (loud) — see GraphFailureMode. */
606
603
  graphFailureMode?: GraphFailureMode;
607
604
  /** Importance score (1-10). */
@@ -691,6 +688,89 @@ interface WikiLintReport {
691
688
  }>;
692
689
  }
693
690
 
691
+ /**
692
+ * Result shape returned by an LLM entity extractor — entities + relations.
693
+ *
694
+ * Lives in shared so both the server-side Memory.store() merge and any
695
+ * client-side enrichment path (`MemoryClient.store(..., { enrichment })`,
696
+ * the MCP `store_memory` tool that goes through MCP sampling) can speak the
697
+ * exact same structural type without crossing the core→client circular
698
+ * dependency.
699
+ */
700
+ interface EntityExtractionResult {
701
+ entities: Array<{
702
+ name: string;
703
+ type: string;
704
+ }>;
705
+ relations: Array<{
706
+ source: string;
707
+ target: string;
708
+ type: string;
709
+ }>;
710
+ }
711
+ /**
712
+ * Merge caller-provided entities/relationships with LLM-extracted ones.
713
+ *
714
+ * Caller-wins on case-insensitive name conflict — the caller's casing is
715
+ * preserved and extracted duplicates are skipped. Extracted relationships
716
+ * are appended only when both endpoints resolve (case-insensitively) to a
717
+ * final entity name; the endpoint strings on the appended relationship are
718
+ * rewritten to the canonical (final) casing.
719
+ *
720
+ * Pure function. Single canonical merge used by Memory.store(),
721
+ * MemoryClient.store() (HTTP client enrichment callback), and the MCP
722
+ * store_memory tool — three call sites cannot drift.
723
+ */
724
+ declare function mergeExtractedEntities(callerEntities: IngestEntity[] | undefined, callerRelationships: IngestRelationship[] | undefined, extracted: EntityExtractionResult): {
725
+ entities: IngestEntity[];
726
+ relationships: IngestRelationship[];
727
+ };
728
+
729
+ interface ApiResponse<T> {
730
+ success: boolean;
731
+ data?: T;
732
+ error?: string;
733
+ }
734
+ /**
735
+ * Build variant of the running pyx-memory image. Detected from the actual
736
+ * availability of `@huggingface/transformers` at boot — `full` keeps the
737
+ * local-embedding stack bundled, `cloud` has it pruned (EMBEDDING_PROVIDER=http
738
+ * is mandatory). The detected value is authoritative; the optional
739
+ * `PYX_BUILD_VARIANT` env stamp is a sanity check that throws on drift.
740
+ */
741
+ type TopologyServiceVariant = 'cloud' | 'full';
742
+ /**
743
+ * Where embedding work happens. `inline` means the server computes vectors
744
+ * in-process (EMBEDDING_PROVIDER=local); `remote` means it delegates to an
745
+ * HTTP embedding service (EMBEDDING_PROVIDER=http). The endpoint URL itself
746
+ * is internal infrastructure and is intentionally NOT exposed here.
747
+ */
748
+ type TopologyEmbeddingLocation = 'inline' | 'remote';
749
+ /**
750
+ * Public topology snapshot returned by `GET /status`. Reflects actual runtime
751
+ * capability (variant detected from import probe, embedding location derived
752
+ * from configured provider) plus the active embedding profile. Never leaks
753
+ * embedding-service URLs or other infrastructure host fragments — those stay
754
+ * behind the admin surface.
755
+ */
756
+ interface Topology {
757
+ service: {
758
+ variant: TopologyServiceVariant;
759
+ /**
760
+ * Operator-declared role label (`PYX_DEPLOYMENT_ROLE`). Free-form string.
761
+ * Structurally absent from the response when the env var is unset or empty.
762
+ */
763
+ declaredRole?: string;
764
+ version: string;
765
+ };
766
+ embedding: {
767
+ location: TopologyEmbeddingLocation;
768
+ modelKey: string;
769
+ modelId: string;
770
+ dimensions: number;
771
+ };
772
+ }
773
+
694
774
  /** Metadata for a single image extracted from a PDF. */
695
775
  interface ExtractedImageMeta {
696
776
  imageId: string;
@@ -956,4 +1036,4 @@ interface PrincipalContext {
956
1036
  /** Sentinel tenant ID used in single-tenant deployments. */
957
1037
  declare const SINGLE_TENANT_ID = "_single";
958
1038
 
959
- export { type AgentId, type ApiResponse, type ConsolidationRunResult, DEFAULTS, EmbeddingProviderName, type EnrichmentCallbacks, type ExtendedMemoryInterface, type GraphFailureMode, type GraphNode, type GraphRelationship, type GraphTraversalResult, type IngestEntity, type IngestErrorEvent, type IngestEvent, type IngestFileOptions, type IngestHeartbeatEvent, type IngestProgressEvent, type IngestRelationship, type IngestResultEvent, type IngestStage, type IngestionResult, 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, SINGLE_TENANT_ID, SensitivityLevel, type StoreInput, StoreTarget, type TemporalQueryFilters, type TenantScopeOptions, type Timestamp, type Topology, type TopologyServiceVariant, VectorProvider, type WikiLintReport };
1039
+ export { type AgentId, type ApiResponse, type ConsolidationRunResult, DEFAULTS, EmbeddingProviderName, type EnrichmentCallbacks, type EntityExtractionResult, type ExtendedMemoryInterface, type GraphFailureMode, type GraphNode, type GraphRelationship, type GraphTraversalResult, type IngestEntity, type IngestErrorEvent, type IngestEvent, type IngestFileOptions, type IngestHeartbeatEvent, type IngestProgressEvent, type IngestRelationship, type IngestResultEvent, type IngestStage, type IngestionResult, 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, SINGLE_TENANT_ID, SensitivityLevel, type StoreInput, StoreTarget, type TemporalQueryFilters, type TenantScopeOptions, type Timestamp, type Topology, type TopologyServiceVariant, VectorProvider, type WikiLintReport, mergeExtractedEntities };
package/dist/index.mjs CHANGED
@@ -1,81 +1,20 @@
1
1
  import {
2
2
  MemoryClient,
3
3
  MemoryServerError
4
- } from "./chunk-CCGW6QRX.mjs";
5
- import "./chunk-7P6ASYW6.mjs";
6
-
7
- // ../shared/src/constants/defaults.ts
8
- var DEFAULTS = {
9
- DATA_DIR: "./data",
10
- VECTOR_PROVIDER: "lancedb",
11
- MEMORY_SERVER_PORT: 7822
12
- };
13
-
14
- // ../shared/src/types/isolation.ts
15
- var NamespaceIsolation = {
16
- SHARED: "shared",
17
- STRICT: "strict"
18
- };
19
-
20
- // ../shared/src/types/memory.ts
21
- var MemoryType = {
22
- SHORT_TERM: "short-term",
23
- LONG_TERM: "long-term",
24
- WORKING: "working",
25
- EPISODIC: "episodic",
26
- SUMMARY: "summary"
27
- };
28
- var SensitivityLevel = {
29
- PUBLIC: "public",
30
- INTERNAL: "internal",
31
- SECRET: "secret"
32
- };
33
- var RAGStrategy = {
34
- NAIVE: "naive",
35
- GRAPH: "graph",
36
- AGENTIC: "agentic",
37
- HYBRID: "hybrid"
38
- };
39
- var VectorProvider = {
40
- LANCEDB: "lancedb"
41
- };
42
- var EmbeddingProviderName = {
43
- STUB: "stub",
44
- /** @deprecated Vestigial — pyx-memory uses internal EmbeddingGemma embeddings. */
45
- ANTHROPIC: "anthropic",
46
- /** @deprecated Vestigial — pyx-memory uses internal EmbeddingGemma embeddings. */
47
- OPENAI: "openai",
48
- /** In-process ONNX model (default: EmbeddingGemma-300M). */
49
- LOCAL: "local",
50
- /** Remote OpenAI-compatible embedding service (pyx-cloud shared, custom, etc.). */
51
- HTTP: "http"
52
- };
53
- var StoreTarget = {
54
- SQLITE: "sqlite",
55
- VECTOR: "vector",
56
- GRAPH: "graph"
57
- };
58
-
59
- // ../shared/src/types/move.ts
60
- var MoveFailureReason = {
61
- /** Entry not found in the caller's tenant. */
62
- NOT_FOUND: "not_found",
63
- /** Move would cross tenant boundary (always forbidden). */
64
- CROSS_TENANT_FORBIDDEN: "cross_tenant_forbidden",
65
- /** Target namespace ID does not exist in the caller's tenant. */
66
- TARGET_NAMESPACE_NOT_FOUND: "target_namespace_not_found",
67
- /** SQLite metadata update failed; no compensation needed. */
68
- SQLITE_UPDATE_FAILED: "sqlite_update_failed",
69
- /** Vector store metadata update failed; SQLite reverted. */
70
- VECTOR_UPDATE_FAILED: "vector_update_failed",
71
- /** Graph edge namespace update failed; SQLite + vector reverted. */
72
- GRAPH_UPDATE_FAILED: "graph_update_failed",
73
- /** Compensation itself failed — manual intervention required. */
74
- COMPENSATION_FAILED: "compensation_failed"
75
- };
76
-
77
- // ../shared/src/types/principal.ts
78
- var SINGLE_TENANT_ID = "_single";
4
+ } from "./chunk-AKQIZAFT.mjs";
5
+ import {
6
+ DEFAULTS,
7
+ EmbeddingProviderName,
8
+ MemoryType,
9
+ MoveFailureReason,
10
+ NamespaceIsolation,
11
+ RAGStrategy,
12
+ SINGLE_TENANT_ID,
13
+ SensitivityLevel,
14
+ StoreTarget,
15
+ VectorProvider,
16
+ mergeExtractedEntities
17
+ } from "./chunk-573W637Y.mjs";
79
18
  export {
80
19
  DEFAULTS,
81
20
  EmbeddingProviderName,
@@ -88,5 +27,6 @@ export {
88
27
  SINGLE_TENANT_ID,
89
28
  SensitivityLevel,
90
29
  StoreTarget,
91
- VectorProvider
30
+ VectorProvider,
31
+ mergeExtractedEntities
92
32
  };
package/dist/react.mjs CHANGED
@@ -11,9 +11,9 @@ import {
11
11
  toGraphologyFormat,
12
12
  transformGraphData,
13
13
  unreachableHealth
14
- } from "./chunk-FSFVD3MB.mjs";
15
- import "./chunk-CCGW6QRX.mjs";
16
- import "./chunk-7P6ASYW6.mjs";
14
+ } from "./chunk-CE3ZBDTM.mjs";
15
+ import "./chunk-AKQIZAFT.mjs";
16
+ import "./chunk-573W637Y.mjs";
17
17
 
18
18
  // ../dashboard/src/hooks/use-consolidation-log.ts
19
19
  import { useCallback as useCallback2, useMemo } from "react";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyxmate/memory",
3
- "version": "0.24.1",
3
+ "version": "0.26.1",
4
4
  "type": "module",
5
5
  "description": "SDK for pyx-memory — Memory as a Service for AI agents",
6
6
  "license": "MIT",
@@ -60,6 +60,7 @@
60
60
  "@napi-rs/keyring": "^1.3.0"
61
61
  },
62
62
  "devDependencies": {
63
+ "@pyx-memory/shared": "workspace:*",
63
64
  "tsup": "^8.4.0"
64
65
  }
65
66
  }
@@ -1,9 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __export = (target, all) => {
3
- for (var name in all)
4
- __defProp(target, name, { get: all[name], enumerable: true });
5
- };
6
-
7
- export {
8
- __export
9
- };