@pyxmate/memory 1.23.1 → 1.23.2

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
@@ -34,10 +34,15 @@ pyx-mem mcp install claude-code --scope user
34
34
 
35
35
  Restart Claude Code. The MCP tools are auto-discovered via MCP Tool Search:
36
36
  `search_memories`, `store_memory`, `get_memory`, `list_memories`,
37
- `delete_memory`, `ingest_memory_file`, `summarize_memory_entity`, `status`,
37
+ `delete_memory`, `summarize_memory_entity`, `status`,
38
38
  `get_user_profile`, `upsert_user_profile`, `record_correction`, and
39
39
  `fetch_applicable_corrections`.
40
40
 
41
+ `ingest_memory_file` is currently supported only through `pyx-mem mcp` connected
42
+ to an upload-enabled self-hosted server (`DATA_PLANE_OPERATION_AUTHORITY_MODE=off`).
43
+ Run `pyx-mem login --endpoint <your-server-url>` first; file ingest is
44
+ unavailable at `https://memory.api.pyxmate.com`.
45
+
41
46
  **That's the whole setup.** Graph is a relational retrieval dimension, not a
42
47
  search-score boost. When content names people, organizations, tools, places,
43
48
  events, or key concepts, pass both `entities` and `relationships` when you can
@@ -907,7 +907,19 @@ function createHttpClient(credentials, fetchImpl = fetch) {
907
907
  if (res.status === 401 || res.status === 403) {
908
908
  return { ok: false, status: res.status, result: mcpAuthFailed(res.status) };
909
909
  }
910
- const text = await res.text().catch(() => "");
910
+ let text;
911
+ try {
912
+ text = await res.text();
913
+ } catch (err) {
914
+ const reason = err instanceof Error ? err.message : String(err);
915
+ return {
916
+ ok: false,
917
+ status: res.status,
918
+ result: mcpEnvelopeError(
919
+ `Failed to read pyx-memory response (HTTP ${res.status}): ${reason}`
920
+ )
921
+ };
922
+ }
911
923
  return parseHttpResponse(res, text);
912
924
  }
913
925
  return {
@@ -933,6 +945,9 @@ function parseHttpResponse(res, text) {
933
945
  if (res.status >= 500) {
934
946
  return { ok: false, status: res.status, result: mcpServerError(res.status, text) };
935
947
  }
948
+ if (res.ok && res.headers.get("content-type")?.includes("application/x-ndjson")) {
949
+ return parseNdjsonOutcome(res.status, text);
950
+ }
936
951
  const parsed = parseJsonText(text);
937
952
  if (!parsed.ok) {
938
953
  if (!res.ok) {
@@ -954,6 +969,31 @@ function parseHttpResponse(res, text) {
954
969
  }
955
970
  return { ok: true, status: res.status, data: parsed.value };
956
971
  }
972
+ function parseNdjsonOutcome(status, text) {
973
+ for (const line of text.split("\n")) {
974
+ if (!line.trim()) continue;
975
+ let frame;
976
+ try {
977
+ frame = JSON.parse(line);
978
+ } catch (err) {
979
+ const reason = err instanceof Error ? err.message : String(err);
980
+ return {
981
+ ok: false,
982
+ status,
983
+ result: mcpEnvelopeError(`pyx-memory sent an invalid NDJSON line: ${reason}`)
984
+ };
985
+ }
986
+ if (frame?.type === "result") return { ok: true, status, data: frame };
987
+ if (frame?.type === "error") {
988
+ return { ok: false, status, result: mcpEnvelopeError(JSON.stringify(frame)) };
989
+ }
990
+ }
991
+ return {
992
+ ok: false,
993
+ status,
994
+ result: mcpEnvelopeError("File ingest stream ended without a server result")
995
+ };
996
+ }
957
997
  function parseJsonText(text) {
958
998
  if (text.length === 0) return { ok: true, value: void 0 };
959
999
  try {
@@ -2175,27 +2215,29 @@ function createProxyServer(client, version, uploadLocalFile) {
2175
2215
  });
2176
2216
  const localUploads = /* @__PURE__ */ new Map();
2177
2217
  let listedOnce = false;
2178
- const refreshFromList = (result) => {
2179
- const tools = result.tools;
2180
- if (Array.isArray(tools)) {
2181
- listedOnce = true;
2182
- for (const tool of tools) {
2183
- const descriptor = uploadLocalFile ? extractLocalUpload(tool._meta) : void 0;
2184
- if (descriptor) localUploads.set(tool.name, descriptor);
2185
- else localUploads.delete(tool.name);
2186
- }
2218
+ let listingsStarted = 0;
2219
+ let listingApplied = 0;
2220
+ const listAndRefresh = async (params) => {
2221
+ const listing = ++listingsStarted;
2222
+ const result = await client.listTools(params);
2223
+ const { tools, nextCursor } = result;
2224
+ if (!Array.isArray(tools) || listing < listingApplied) return result;
2225
+ listingApplied = listing;
2226
+ listedOnce = true;
2227
+ if (params?.cursor === void 0 && nextCursor === void 0) localUploads.clear();
2228
+ for (const tool of tools) {
2229
+ const descriptor = uploadLocalFile ? extractLocalUpload(tool._meta) : void 0;
2230
+ if (descriptor) localUploads.set(tool.name, descriptor);
2231
+ else localUploads.delete(tool.name);
2187
2232
  }
2188
2233
  return result;
2189
2234
  };
2190
2235
  if (caps?.tools) {
2191
- server.setRequestHandler(
2192
- ListToolsRequestSchema,
2193
- async (req) => refreshFromList(await client.listTools(req.params))
2194
- );
2236
+ server.setRequestHandler(ListToolsRequestSchema, (req) => listAndRefresh(req.params));
2195
2237
  server.setRequestHandler(CallToolRequestSchema, async (req) => {
2196
2238
  if (uploadLocalFile && !listedOnce) {
2197
2239
  try {
2198
- refreshFromList(await client.listTools());
2240
+ await listAndRefresh();
2199
2241
  } catch {
2200
2242
  }
2201
2243
  }
@@ -2223,7 +2265,7 @@ function createProxyServer(client, version, uploadLocalFile) {
2223
2265
  return server;
2224
2266
  }
2225
2267
  async function runMcpProxyServer(opts) {
2226
- const version = opts.version ?? (true ? "1.23.1" : "0.0.0-dev");
2268
+ const version = opts.version ?? (true ? "1.23.2" : "0.0.0-dev");
2227
2269
  const read = await opts.readCredentials();
2228
2270
  if (!read.ok) {
2229
2271
  throw new Error(
package/dist/index.d.ts CHANGED
@@ -1650,7 +1650,11 @@ interface EnrichmentPendingV1 {
1650
1650
  interface EnrichmentTextWindowsTruncation {
1651
1651
  /** Total characters seen by the parser, including content beyond the cap. */
1652
1652
  originalChars: number;
1653
- /** Sum of emitted window lengths. <= originalChars. */
1653
+ /**
1654
+ * Sum of emitted window lengths. Overlapping regions between adjacent
1655
+ * windows are counted once per window, so this can exceed originalChars
1656
+ * once windowCount >= 2; bounded by windowCount * originalChars.
1657
+ */
1654
1658
  includedChars: number;
1655
1659
  windowCount: number;
1656
1660
  truncated: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyxmate/memory",
3
- "version": "1.23.1",
3
+ "version": "1.23.2",
4
4
  "type": "module",
5
5
  "description": "SDK for pyx-memory — Memory as a Service for AI agents",
6
6
  "license": "MIT",