@pyxmate/memory 1.17.16 → 1.17.18

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,5 +1,6 @@
1
1
  // src/contract/index.ts
2
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
+ 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.`;
3
4
  var PERSISTENT_MEMORY_SECTION = [
4
5
  "## Persistent Memory",
5
6
  "",
@@ -159,6 +160,7 @@ function buildAgentSnippet() {
159
160
 
160
161
  export {
161
162
  PYX_MEMORY_INSTRUCTIONS,
163
+ PYX_MEMORY_COMPANION_CORE_INSTRUCTIONS,
162
164
  PERSISTENT_MEMORY_SECTION,
163
165
  buildDesignGuide,
164
166
  AGENT_TARGETS,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  assertGraphExtractionPayload,
3
3
  mergeExtractedEntities
4
- } from "./chunk-34MTVIYK.mjs";
4
+ } from "./chunk-3OLH3HYR.mjs";
5
5
 
6
6
  // ../shared/src/constants/defaults.ts
7
7
  var DEFAULTS = {
@@ -11,6 +11,21 @@ var DEFAULTS = {
11
11
  };
12
12
  var TAXONOMY_MAX_CATEGORIES = 10;
13
13
 
14
+ // ../shared/src/document-source.ts
15
+ import { createHash } from "crypto";
16
+ function documentSource(prefix, documentKey) {
17
+ return `${prefix}:${createHash("sha256").update(documentKey).digest("hex")}`;
18
+ }
19
+ function documentGraphSource(documentKey) {
20
+ return documentSource("document-graph", documentKey);
21
+ }
22
+ function documentContentSource(documentKey) {
23
+ return documentSource("document-content", documentKey);
24
+ }
25
+ function documentImageSource(documentKey) {
26
+ return documentSource("document-image", documentKey);
27
+ }
28
+
14
29
  // ../shared/src/mcp/search-response.ts
15
30
  var STRIPPED_ENTRY_FIELDS = /* @__PURE__ */ new Set(["contentHash", "embedding", "tenantId", "userId", "teamId"]);
16
31
  function isRecord(value) {
@@ -228,12 +243,54 @@ var DisabledMemory = class {
228
243
  };
229
244
 
230
245
  // ../client/src/memory-client.ts
246
+ function isRecord3(value) {
247
+ return value !== null && typeof value === "object" && !Array.isArray(value);
248
+ }
249
+ function normalizeRetryAfter(retryAfter, retryAfterSeconds) {
250
+ const exact = typeof retryAfter === "string" ? retryAfter.trim() : "";
251
+ if (exact) {
252
+ const parsed = /^\d+$/.test(exact) ? Number(exact) : Math.ceil((Date.parse(exact) - Date.now()) / 1e3);
253
+ return {
254
+ retryAfter: exact,
255
+ ...Number.isSafeInteger(parsed) && parsed >= 0 ? { retryAfterSeconds: parsed } : {}
256
+ };
257
+ }
258
+ return Number.isSafeInteger(retryAfterSeconds) && retryAfterSeconds >= 0 ? { retryAfterSeconds } : {};
259
+ }
260
+ function structuralHttpStatus(error) {
261
+ if (!isRecord3(error) || typeof error.status !== "number") return void 0;
262
+ return Number.isSafeInteger(error.status) && error.status >= 400 && error.status < 600 ? error.status : void 0;
263
+ }
264
+ function isFileIngestResult(value) {
265
+ if (!isRecord3(value)) return false;
266
+ if (typeof value.filename !== "string" || typeof value.fileType !== "string" || typeof value.chunks !== "number" || !Number.isSafeInteger(value.chunks) || value.chunks < 0 || !Array.isArray(value.entryIds) || !value.entryIds.every((id) => typeof id === "string") || typeof value.totalCharacters !== "number" || !Number.isSafeInteger(value.totalCharacters) || value.totalCharacters < 0) {
267
+ return false;
268
+ }
269
+ if (value.graphAnchorEntryId !== void 0 && typeof value.graphAnchorEntryId !== "string") {
270
+ return false;
271
+ }
272
+ if (value.enrichment !== void 0 && !isRecord3(value.enrichment)) return false;
273
+ return true;
274
+ }
275
+ function hasEnrichmentCallback(callbacks) {
276
+ return Boolean(callbacks?.describeImage || callbacks?.extractEntitiesV2);
277
+ }
231
278
  var MemoryServerError = class extends Error {
232
279
  status;
233
- constructor(message, status) {
280
+ /** Stable machine-readable discriminator returned by the memory server. */
281
+ code;
282
+ /** Exact HTTP Retry-After value returned by the memory server. */
283
+ retryAfter;
284
+ /** Retry delay normalized to seconds when Retry-After is parseable. */
285
+ retryAfterSeconds;
286
+ constructor(message, status, code, retryAfter) {
234
287
  super(message);
235
288
  this.name = "MemoryServerError";
236
289
  this.status = status;
290
+ this.code = code;
291
+ const retry = normalizeRetryAfter(retryAfter);
292
+ this.retryAfter = retry.retryAfter;
293
+ this.retryAfterSeconds = retry.retryAfterSeconds;
237
294
  }
238
295
  /** True when the server returned HTTP 404 (not found). */
239
296
  get isNotFound() {
@@ -273,12 +330,42 @@ var MemoryClient = class {
273
330
  }
274
331
  authorityHeaders(options) {
275
332
  if (!options) return {};
333
+ const headers = {};
276
334
  const key = options.idempotencyKey;
277
- if (key === void 0) return {};
278
- if (typeof key !== "string" || key.length === 0 || !key.isWellFormed() || new TextEncoder().encode(key).byteLength > 256) {
279
- throw new Error("idempotencyKey must be 1-256 well-formed UTF-8 bytes");
335
+ if (key !== void 0) {
336
+ if (typeof key !== "string" || key.length === 0 || !key.isWellFormed() || new TextEncoder().encode(key).byteLength > 256) {
337
+ throw new Error("idempotencyKey must be 1-256 well-formed UTF-8 bytes");
338
+ }
339
+ headers["Idempotency-Key"] = key;
340
+ }
341
+ for (const [headerName, value] of [
342
+ ["X-Tenant-Id", options.tenantId],
343
+ ["X-Namespace-Id", options.namespaceId]
344
+ ]) {
345
+ if (value === void 0) continue;
346
+ const configured = Object.entries(this._authHeaders).find(
347
+ ([name]) => name.toLowerCase() === headerName.toLowerCase()
348
+ )?.[1];
349
+ if (configured !== void 0) {
350
+ if (configured !== value) {
351
+ throw new Error(
352
+ `${headerName} conflicts with the MemoryClient default scope; create a client for the requested scope`
353
+ );
354
+ }
355
+ continue;
356
+ }
357
+ headers[headerName] = value;
280
358
  }
281
- return { "Idempotency-Key": key };
359
+ return headers;
360
+ }
361
+ exactReadAuthority(namespaceId, authority) {
362
+ if (namespaceId !== void 0 && authority?.namespaceId !== void 0 && namespaceId !== authority.namespaceId) {
363
+ throw new Error("X-Namespace-Id conflicts with the request authority scope");
364
+ }
365
+ return {
366
+ ...authority,
367
+ ...namespaceId !== void 0 ? { namespaceId } : {}
368
+ };
282
369
  }
283
370
  async initialize() {
284
371
  const response = await fetch(`${this.baseUrl}/health`, {
@@ -337,7 +424,7 @@ var MemoryClient = class {
337
424
  if (params.abstentionThreshold != null)
338
425
  searchParams.set("abstentionThreshold", String(params.abstentionThreshold));
339
426
  return this.fetchApi(`/api/memory/search?${searchParams}`, {
340
- headers: this.authorityHeaders(authority)
427
+ headers: this.authorityHeaders(this.exactReadAuthority(params.namespaceId, authority))
341
428
  });
342
429
  }
343
430
  async get(id, authority) {
@@ -403,7 +490,7 @@ var MemoryClient = class {
403
490
  }
404
491
  const qs = searchParams.toString();
405
492
  return this.fetchApi(`/api/memory/entries${qs ? `?${qs}` : ""}`, {
406
- headers: this.authorityHeaders(authority)
493
+ headers: this.authorityHeaders(this.exactReadAuthority(params.namespaceId, authority))
407
494
  });
408
495
  }
409
496
  // --- File ingest ---
@@ -426,6 +513,16 @@ var MemoryClient = class {
426
513
  * method by design (one wire format, one SDK method).
427
514
  */
428
515
  async *ingestFileEvents(file, options) {
516
+ if (options?.documentKey !== void 0) {
517
+ if (typeof options.documentKey !== "string" || options.documentKey.trim().length === 0) {
518
+ throw new Error("ingestFileEvents requires a non-empty documentKey when provided");
519
+ }
520
+ if (!options.enrichment?.extractEntitiesV2) {
521
+ throw new Error(
522
+ "ingestFileEvents with documentKey requires enrichment.extractEntitiesV2 so the stable graph replacement can be finalized"
523
+ );
524
+ }
525
+ }
429
526
  const controller = new AbortController();
430
527
  const relayAbort = () => controller.abort(options?.signal?.reason);
431
528
  if (options?.signal?.aborted) relayAbort();
@@ -435,6 +532,12 @@ var MemoryClient = class {
435
532
  formData.append("file", file);
436
533
  if (options?.description) formData.append("description", options.description);
437
534
  if (options?.namespaceId) formData.append("namespaceId", options.namespaceId);
535
+ if (options?.documentKey !== void 0) {
536
+ formData.append("documentKey", options.documentKey);
537
+ }
538
+ if (options?.catalogEntryId !== void 0) {
539
+ formData.append("catalogEntryId", options.catalogEntryId);
540
+ }
438
541
  const wantsTextWindows = Boolean(options?.enrichment?.extractEntitiesV2);
439
542
  const headers = {
440
543
  Accept: "application/x-ndjson",
@@ -490,6 +593,9 @@ var MemoryClient = class {
490
593
  const formData = new FormData();
491
594
  formData.append("file", file);
492
595
  formData.append("documentKey", options.documentKey);
596
+ if (options.catalogEntryId !== void 0) {
597
+ formData.append("catalogEntryId", options.catalogEntryId);
598
+ }
493
599
  if (options.namespaceId) formData.append("namespaceId", options.namespaceId);
494
600
  const headers = {
495
601
  Accept: "application/x-ndjson",
@@ -519,7 +625,7 @@ var MemoryClient = class {
519
625
  "graph-only"
520
626
  );
521
627
  const data = outcome.data;
522
- if (data?.purpose !== "graph-only" || !Array.isArray(data.entryIds)) {
628
+ if (data?.purpose !== "graph-only" || !Array.isArray(data.entryIds) || data.entryIds.length !== 1 || typeof data.entryIds[0] !== "string" || data.entryIds[0].length === 0 || typeof data.graphAnchorEntryId !== "string" || data.graphAnchorEntryId.length === 0) {
523
629
  throw new MemoryServerError(
524
630
  "Graph enrichment finalization returned an unexpected result",
525
631
  0
@@ -557,6 +663,17 @@ var MemoryClient = class {
557
663
  return null;
558
664
  }
559
665
  const contentType = res.headers.get("content-type") ?? "";
666
+ if (!res.ok) {
667
+ let error;
668
+ try {
669
+ await this.parseApiResponse(res);
670
+ error = new MemoryServerError(`Memory server error: ${res.status}`, res.status);
671
+ } catch (cause) {
672
+ error = cause;
673
+ }
674
+ yield this.ingestErrorEvent(error, "parsing");
675
+ return null;
676
+ }
560
677
  if (!contentType.includes("application/x-ndjson")) {
561
678
  yield this.ingestErrorEvent(
562
679
  new MemoryServerError(
@@ -602,6 +719,8 @@ var MemoryClient = class {
602
719
  break;
603
720
  }
604
721
  if (type === "error") {
722
+ const partialResult = isFileIngestResult(raw.partialResult) ? raw.partialResult : void 0;
723
+ const retry = normalizeRetryAfter(raw.retryAfter, raw.retryAfterSeconds);
605
724
  yield {
606
725
  schemaVersion: 1,
607
726
  type: "error",
@@ -609,7 +728,9 @@ var MemoryClient = class {
609
728
  error: typeof raw.error === "string" ? raw.error : "File ingest failed",
610
729
  message: typeof raw.message === "string" ? raw.message : void 0,
611
730
  code: typeof raw.code === "string" || typeof raw.code === "number" ? raw.code : void 0,
612
- status: typeof raw.status === "number" ? raw.status : void 0
731
+ status: typeof raw.status === "number" ? raw.status : void 0,
732
+ ...retry,
733
+ ...partialResult ? { partialResult } : {}
613
734
  };
614
735
  return null;
615
736
  }
@@ -640,26 +761,48 @@ var MemoryClient = class {
640
761
  */
641
762
  async *completeIngestFileEvents(file, result, options, signal) {
642
763
  try {
643
- if (!result.enrichment || !options?.enrichment) {
764
+ const callbacks = options?.enrichment;
765
+ if (options?.documentKey !== void 0 && !result.enrichment) {
766
+ throw new MemoryServerError(
767
+ "documentKey-aware ingest response omitted required enrichment preparation; stable graph finalization did not run",
768
+ 502
769
+ );
770
+ }
771
+ if (!result.enrichment || !hasEnrichmentCallback(callbacks)) {
644
772
  yield { schemaVersion: 1, type: "result", stage: "complete", ...result };
645
773
  return;
646
774
  }
647
775
  const outcome = yield* this.runEnrichmentCallbacks(
648
776
  file,
649
777
  result.enrichment,
650
- options.enrichment,
778
+ callbacks,
651
779
  signal,
652
- "ingest"
780
+ "ingest",
781
+ options?.documentKey !== void 0
653
782
  );
654
783
  if (outcome.posted) {
655
784
  const enrichData = outcome.data;
785
+ if (options?.documentKey !== void 0 && !enrichData.graphAnchorEntryId) {
786
+ throw new MemoryServerError(
787
+ "documentKey-aware enrichment response omitted graphAnchorEntryId; server does not support stable document replacement",
788
+ 502
789
+ );
790
+ }
656
791
  result.entryIds.push(...enrichData.entryIds);
792
+ if (enrichData.graphAnchorEntryId) {
793
+ result.graphAnchorEntryId = enrichData.graphAnchorEntryId;
794
+ }
795
+ if ((enrichData.relationshipsDropped ?? 0) > 0) {
796
+ result.relationshipsDropped = enrichData.relationshipsDropped;
797
+ result.droppedRelationships = enrichData.droppedRelationships;
798
+ }
657
799
  result.chunks += outcome.descriptions.length;
658
800
  result.totalCharacters += outcome.descriptions.reduce(
659
801
  (sum, d) => sum + d.description.length,
660
802
  0
661
803
  );
662
804
  }
805
+ delete result.enrichment;
663
806
  yield { schemaVersion: 1, type: "result", stage: "complete", ...result };
664
807
  } catch (err) {
665
808
  yield this.ingestErrorEvent(err, "enrichment", result);
@@ -673,16 +816,24 @@ var MemoryClient = class {
673
816
  * heartbeat events around each slow step. Throws on any failure; the
674
817
  * purpose-specific wrappers translate that into their terminal error.
675
818
  *
676
- * The purpose controls empty-output semantics: ingest skips an optional
677
- * empty add-on; graph-only finalizes an extractor that ran and found zero
678
- * entities, but refuses to erase prior graph state when no extractable input
679
- * reached the callback at all.
819
+ * Legacy ingest keeps its existing partial add-on behavior. Graph-only and
820
+ * documentKey-aware full ingest may replace a prior graph only from a
821
+ * complete input set (no truncated text windows or undescribed images). A
822
+ * complete extractor result containing zero entities remains a valid clear.
680
823
  */
681
824
  // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: image-describe / extract-entities-v2 / no-op are documented decision branches; the fan-out covers the v0.15.0 enrichment phase + heartbeat plumbing.
682
- async *runEnrichmentCallbacks(file, enrichment, callbacks, signal, purpose) {
825
+ async *runEnrichmentCallbacks(file, enrichment, callbacks, signal, purpose, replaceEmptyGraph = false) {
683
826
  const { fileId, token, expiresAt, images } = enrichment;
684
827
  const isV2 = "version" in enrichment;
685
828
  const textWindows = isV2 ? enrichment.textWindows : [];
829
+ const textWindowsTruncation = isV2 ? enrichment.textWindowsTruncation : void 0;
830
+ const requiresCompleteGraphReplacement = purpose === "graph-only" || replaceEmptyGraph;
831
+ if (requiresCompleteGraphReplacement && textWindowsTruncation?.truncated) {
832
+ throw new MemoryServerError(
833
+ `Graph enrichment input was truncated (${textWindowsTruncation.reason ?? "unknown limit"}); prior graph state was preserved and the pending session remains retryable`,
834
+ 422
835
+ );
836
+ }
686
837
  const descriptions = [];
687
838
  const describeImage = callbacks.describeImage;
688
839
  if (describeImage && images.length > 0) {
@@ -731,6 +882,12 @@ var MemoryClient = class {
731
882
  };
732
883
  }
733
884
  }
885
+ if (requiresCompleteGraphReplacement && descriptions.length < images.length) {
886
+ throw new MemoryServerError(
887
+ `Graph enrichment described only ${descriptions.length}/${images.length} images; prior graph state was preserved and the pending session remains retryable`,
888
+ 422
889
+ );
890
+ }
734
891
  let entities;
735
892
  let relationships;
736
893
  const imageDescriptionTexts = descriptions.map((d) => d.description);
@@ -773,7 +930,7 @@ var MemoryClient = class {
773
930
  }
774
931
  const hasGraph = (entities?.length ?? 0) > 0;
775
932
  const hasImages = descriptions.length > 0;
776
- if (!hasGraph && !hasImages && purpose === "ingest") {
933
+ if (!hasGraph && !hasImages && purpose === "ingest" && !replaceEmptyGraph) {
777
934
  return { posted: false, data: null, descriptions };
778
935
  }
779
936
  yield {
@@ -793,17 +950,14 @@ var MemoryClient = class {
793
950
  ...this._authHeaders
794
951
  },
795
952
  signal,
796
- body: JSON.stringify({ imageDescriptions: descriptions, entities, relationships })
953
+ body: JSON.stringify({
954
+ imageDescriptions: descriptions,
955
+ entities: entities ?? [],
956
+ relationships: relationships ?? []
957
+ })
797
958
  }),
798
959
  signal
799
960
  );
800
- if (!enrichRes.ok) {
801
- const body = await enrichRes.json().catch(() => ({}));
802
- throw new MemoryServerError(
803
- body.error ?? `Enrichment failed: ${enrichRes.status}`,
804
- enrichRes.status
805
- );
806
- }
807
961
  const data = await this.parseApiResponse(enrichRes);
808
962
  return { posted: true, data, descriptions };
809
963
  }
@@ -869,15 +1023,20 @@ var MemoryClient = class {
869
1023
  return result;
870
1024
  }
871
1025
  ingestErrorEvent(error, stage, partialResult) {
872
- const status = error instanceof MemoryServerError ? error.status : error instanceof Error && error.name === "AbortError" ? 499 : void 0;
1026
+ const structuralError = isRecord3(error) ? error : void 0;
1027
+ const status = error instanceof MemoryServerError ? error.status : error instanceof Error && error.name === "AbortError" ? 499 : structuralHttpStatus(error);
873
1028
  const message = error instanceof Error ? error.message : String(error);
1029
+ const code = error instanceof MemoryServerError ? error.code : void 0;
1030
+ const retry = error instanceof MemoryServerError ? { retryAfter: error.retryAfter, retryAfterSeconds: error.retryAfterSeconds } : normalizeRetryAfter(structuralError?.retryAfter, structuralError?.retryAfterSeconds);
874
1031
  return {
875
1032
  schemaVersion: 1,
876
1033
  type: "error",
877
1034
  stage,
878
1035
  error: message,
879
1036
  message,
880
- ...status != null ? { status, code: status } : {},
1037
+ ...status != null ? { status, code: code ?? status } : {},
1038
+ ...retry.retryAfter !== void 0 ? { retryAfter: retry.retryAfter } : {},
1039
+ ...retry.retryAfterSeconds !== void 0 ? { retryAfterSeconds: retry.retryAfterSeconds } : {},
881
1040
  ...partialResult ? { partialResult } : {}
882
1041
  };
883
1042
  }
@@ -885,15 +1044,24 @@ var MemoryClient = class {
885
1044
  * Get the download URL for an uploaded file.
886
1045
  * Returns a URL that serves the original file binary with proper Content-Type.
887
1046
  */
888
- getFileDownloadUrl(filename) {
889
- return `${this.baseUrl}/api/memory/files/download/${encodeURIComponent(filename)}`;
1047
+ getFileDownloadUrl(filename, options) {
1048
+ const url = new URL(
1049
+ `${this.baseUrl}/api/memory/files/download/${encodeURIComponent(filename)}`
1050
+ );
1051
+ if (options?.documentKey !== void 0) {
1052
+ url.searchParams.set("documentKey", options.documentKey);
1053
+ }
1054
+ if (options?.namespaceId !== void 0) {
1055
+ url.searchParams.set("namespaceId", options.namespaceId);
1056
+ }
1057
+ return url.toString();
890
1058
  }
891
1059
  /**
892
1060
  * Download an uploaded file by filename.
893
1061
  * Returns the raw Response (caller handles the body — arrayBuffer, blob, stream, etc.).
894
1062
  */
895
- async downloadFile(filename) {
896
- const url = this.getFileDownloadUrl(filename);
1063
+ async downloadFile(filename, options) {
1064
+ const url = this.getFileDownloadUrl(filename, options);
897
1065
  const res = await fetch(url, { headers: this._authHeaders });
898
1066
  if (!res.ok) {
899
1067
  throw new MemoryServerError(`File download failed: ${res.status}`, res.status);
@@ -971,10 +1139,10 @@ var MemoryClient = class {
971
1139
  async repairGraph() {
972
1140
  return this.fetchApi("/api/memory/graph/repair", { method: "POST" });
973
1141
  }
974
- async deleteBySource(source) {
1142
+ async deleteBySource(source, authority) {
975
1143
  const result = await this.fetchApi(
976
1144
  `/api/memory/source/${this.encodePathSegment(source)}`,
977
- { method: "DELETE" }
1145
+ { method: "DELETE", headers: this.authorityHeaders(authority) }
978
1146
  );
979
1147
  return result.deleted;
980
1148
  }
@@ -987,14 +1155,20 @@ var MemoryClient = class {
987
1155
  }
988
1156
  );
989
1157
  }
990
- async queryAsOf(asOfDate, filters = {}) {
1158
+ async queryAsOf(asOfDate, filters = {}, authority) {
1159
+ if (filters.cursor !== void 0 && filters.offset !== void 0) {
1160
+ throw new Error("queryAsOf cursor and offset are mutually exclusive");
1161
+ }
991
1162
  const params = new URLSearchParams({ asOf: asOfDate });
992
1163
  if (filters.type) params.set("type", filters.type);
993
1164
  if (filters.agentId) params.set("agentId", filters.agentId);
994
1165
  if (filters.source) params.set("source", filters.source);
995
1166
  if (filters.limit) params.set("limit", String(filters.limit));
1167
+ if (filters.offset !== void 0) params.set("offset", String(filters.offset));
1168
+ if (filters.cursor !== void 0) params.set("cursor", filters.cursor);
996
1169
  const result = await this.fetchApi(
997
- `/api/memory/query-as-of?${params}`
1170
+ `/api/memory/query-as-of?${params}`,
1171
+ { headers: this.authorityHeaders(authority) }
998
1172
  );
999
1173
  return result.entries;
1000
1174
  }
@@ -1036,11 +1210,15 @@ var MemoryClient = class {
1036
1210
  return result.entries;
1037
1211
  }
1038
1212
  async queryByEventTime(startTime, endTime, filters = {}) {
1213
+ if (filters.cursor !== void 0) {
1214
+ throw new Error("queryByEventTime does not support a createdAt cursor; use offset");
1215
+ }
1039
1216
  const params = new URLSearchParams({ startTime, endTime });
1040
1217
  if (filters.type) params.set("type", filters.type);
1041
1218
  if (filters.agentId) params.set("agentId", filters.agentId);
1042
1219
  if (filters.source) params.set("source", filters.source);
1043
1220
  if (filters.limit) params.set("limit", String(filters.limit));
1221
+ if (filters.offset !== void 0) params.set("offset", String(filters.offset));
1044
1222
  const result = await this.fetchApi(
1045
1223
  `/api/memory/query-by-event-time?${params}`
1046
1224
  );
@@ -1164,23 +1342,33 @@ var MemoryClient = class {
1164
1342
  }
1165
1343
  /** Parse and validate a JSON API response, throwing MemoryServerError on any failure. */
1166
1344
  async parseApiResponse(res) {
1345
+ const retryAfter = res.headers.get("Retry-After") ?? void 0;
1167
1346
  let body;
1168
1347
  try {
1169
1348
  body = await res.json();
1170
1349
  } catch {
1171
1350
  throw new MemoryServerError(
1172
1351
  `Memory server error: invalid JSON response (${res.status})`,
1173
- res.status
1352
+ res.status,
1353
+ void 0,
1354
+ retryAfter
1174
1355
  );
1175
1356
  }
1176
- if (!body?.success || body.data == null) {
1357
+ if (!res.ok || !body?.success || body.data == null) {
1177
1358
  if (res.ok && body?.error === void 0) {
1178
1359
  throw new MemoryServerError(
1179
1360
  `Memory server returned HTTP ${res.status} without the expected { success, data } API envelope. Check that the base URL points to a pyx-memory instance server; hosted pyx-memory access uses MCP.`,
1180
- res.status
1361
+ res.status,
1362
+ void 0,
1363
+ retryAfter
1181
1364
  );
1182
1365
  }
1183
- throw new MemoryServerError(body?.error ?? `Memory server error: ${res.status}`, res.status);
1366
+ throw new MemoryServerError(
1367
+ body?.error ?? `Memory server error: ${res.status}`,
1368
+ res.status,
1369
+ body?.code,
1370
+ retryAfter
1371
+ );
1184
1372
  }
1185
1373
  return body.data;
1186
1374
  }
@@ -1189,6 +1377,9 @@ var MemoryClient = class {
1189
1377
  export {
1190
1378
  DEFAULTS,
1191
1379
  TAXONOMY_MAX_CATEGORIES,
1380
+ documentGraphSource,
1381
+ documentContentSource,
1382
+ documentImageSource,
1192
1383
  projectSearchResponseForMcp,
1193
1384
  secretElevationNoticeFor,
1194
1385
  withSecretElevationNotice,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  MemoryClient
3
- } from "./chunk-MDFUZ3V2.mjs";
3
+ } from "./chunk-H6ZLMPZH.mjs";
4
4
 
5
5
  // ../dashboard/src/aggregations/consolidation-analytics.ts
6
6
  function analyzeConsolidationLog(entries) {
@@ -178,8 +178,10 @@ var DashboardClient = class extends MemoryClient {
178
178
  * arbitrary N-node sample were silently dropped, collapsing dense graphs to
179
179
  * a handful of rendered links.
180
180
  */
181
- async graphSubgraph(edges = 2e3) {
182
- return this.fetchApi(`/api/memory/graph/subgraph?edges=${edges}`);
181
+ async graphSubgraph(edges = 2e3, nodes = 2e3) {
182
+ return this.fetchApi(
183
+ `/api/memory/graph/subgraph?edges=${edges}&nodes=${nodes}`
184
+ );
183
185
  }
184
186
  async fetchHealthRaw() {
185
187
  return this.fetchApi("/health");
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  PERSISTENT_MEMORY_SECTION,
4
4
  buildDesignGuide
5
- } from "../chunk-FQW6BQ2N.mjs";
5
+ } from "../chunk-EZERG25I.mjs";
6
6
 
7
7
  // src/cli/exit-codes.ts
8
8
  var EXIT = {
@@ -597,6 +597,7 @@ function createHttpClient(credentials, fetchImpl = fetch) {
597
597
  headers.set("Accept", "application/json");
598
598
  if (contentType) headers.set("Content-Type", contentType);
599
599
  if (scope?.tenantId) headers.set("X-Tenant-Id", scope.tenantId);
600
+ if (scope?.namespaceId) headers.set("X-Namespace-Id", scope.namespaceId);
600
601
  if (scope?.userId) headers.set("X-User-Id", scope.userId);
601
602
  if (scope?.teamId) headers.set("X-Team-Id", scope.teamId);
602
603
  if (scope?.callerAccessLevel) headers.set("X-Caller-Access-Level", scope.callerAccessLevel);
@@ -835,7 +836,7 @@ function createProxyServer(client, version, uploadLocalFile) {
835
836
  return server;
836
837
  }
837
838
  async function runMcpProxyServer(opts) {
838
- const version = opts.version ?? (true ? "1.17.16" : "0.0.0-dev");
839
+ const version = opts.version ?? (true ? "1.17.18" : "0.0.0-dev");
839
840
  const read = await opts.readCredentials();
840
841
  if (!read.ok) {
841
842
  const text = read.result.content.map((c) => c.type === "text" ? c.text : "").join(" ").trim();
@@ -21,6 +21,8 @@ interface SubgraphResponse {
21
21
  totalNodes: number;
22
22
  totalEdges: number;
23
23
  truncated: boolean;
24
+ nodesTruncated?: boolean;
25
+ edgesTruncated?: boolean;
24
26
  }
25
27
  interface HealthData {
26
28
  status: 'ok' | 'degraded' | 'unreachable';
@@ -191,7 +193,7 @@ declare class DashboardClient extends MemoryClient {
191
193
  * arbitrary N-node sample were silently dropped, collapsing dense graphs to
192
194
  * a handful of rendered links.
193
195
  */
194
- graphSubgraph(edges?: number): Promise<SubgraphResponse>;
196
+ graphSubgraph(edges?: number, nodes?: number): Promise<SubgraphResponse>;
195
197
  fetchHealthRaw(): Promise<RawHealthResponse>;
196
198
  }
197
199
 
@@ -11,9 +11,9 @@ import {
11
11
  toGraphologyFormat,
12
12
  transformGraphData,
13
13
  unreachableHealth
14
- } from "./chunk-ZVI7DCB4.mjs";
15
- import "./chunk-MDFUZ3V2.mjs";
16
- import "./chunk-34MTVIYK.mjs";
14
+ } from "./chunk-T35ZOOES.mjs";
15
+ import "./chunk-H6ZLMPZH.mjs";
16
+ import "./chunk-3OLH3HYR.mjs";
17
17
  export {
18
18
  DashboardClient,
19
19
  Poller,