@pyxmate/memory 1.17.15 → 1.17.17
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/dist/chunk-3OLH3HYR.mjs +1404 -0
- package/dist/{chunk-WDF5LAZS.mjs → chunk-AMKPOPH6.mjs} +5 -3
- package/dist/{chunk-LGNSLDGB.mjs → chunk-JGFDID3B.mjs} +527 -211
- package/dist/cli/pyx-mem.mjs +2 -1
- package/dist/dashboard.d.ts +3 -1
- package/dist/dashboard.mjs +3 -3
- package/dist/data-plane-contract-fDvTm9bF.d.ts +245 -0
- package/dist/data-plane-contract.d.ts +1 -235
- package/dist/data-plane-contract.mjs +15 -1255
- package/dist/index.d.ts +204 -19
- package/dist/index.mjs +15 -3
- package/dist/react.mjs +3 -3
- package/package.json +1 -1
- package/dist/chunk-A3L46P2G.mjs +0 -57
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
+
assertGraphExtractionPayload,
|
|
2
3
|
mergeExtractedEntities
|
|
3
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3OLH3HYR.mjs";
|
|
4
5
|
|
|
5
6
|
// ../shared/src/constants/defaults.ts
|
|
6
7
|
var DEFAULTS = {
|
|
@@ -10,6 +11,21 @@ var DEFAULTS = {
|
|
|
10
11
|
};
|
|
11
12
|
var TAXONOMY_MAX_CATEGORIES = 10;
|
|
12
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
|
+
|
|
13
29
|
// ../shared/src/mcp/search-response.ts
|
|
14
30
|
var STRIPPED_ENTRY_FIELDS = /* @__PURE__ */ new Set(["contentHash", "embedding", "tenantId", "userId", "teamId"]);
|
|
15
31
|
function isRecord(value) {
|
|
@@ -227,12 +243,54 @@ var DisabledMemory = class {
|
|
|
227
243
|
};
|
|
228
244
|
|
|
229
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
|
+
}
|
|
230
278
|
var MemoryServerError = class extends Error {
|
|
231
279
|
status;
|
|
232
|
-
|
|
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) {
|
|
233
287
|
super(message);
|
|
234
288
|
this.name = "MemoryServerError";
|
|
235
289
|
this.status = status;
|
|
290
|
+
this.code = code;
|
|
291
|
+
const retry = normalizeRetryAfter(retryAfter);
|
|
292
|
+
this.retryAfter = retry.retryAfter;
|
|
293
|
+
this.retryAfterSeconds = retry.retryAfterSeconds;
|
|
236
294
|
}
|
|
237
295
|
/** True when the server returned HTTP 404 (not found). */
|
|
238
296
|
get isNotFound() {
|
|
@@ -272,12 +330,33 @@ var MemoryClient = class {
|
|
|
272
330
|
}
|
|
273
331
|
authorityHeaders(options) {
|
|
274
332
|
if (!options) return {};
|
|
333
|
+
const headers = {};
|
|
275
334
|
const key = options.idempotencyKey;
|
|
276
|
-
if (key
|
|
277
|
-
|
|
278
|
-
|
|
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;
|
|
279
358
|
}
|
|
280
|
-
return
|
|
359
|
+
return headers;
|
|
281
360
|
}
|
|
282
361
|
async initialize() {
|
|
283
362
|
const response = await fetch(`${this.baseUrl}/health`, {
|
|
@@ -424,8 +503,17 @@ var MemoryClient = class {
|
|
|
424
503
|
* collect the terminal event; there is no separate `ingestFile()` Promise
|
|
425
504
|
* method by design (one wire format, one SDK method).
|
|
426
505
|
*/
|
|
427
|
-
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: NDJSON dispatch + SDK enrichment fan-out is two cooperating paths; documented inline and tested in memory-client-events.test.ts.
|
|
428
506
|
async *ingestFileEvents(file, options) {
|
|
507
|
+
if (options?.documentKey !== void 0) {
|
|
508
|
+
if (typeof options.documentKey !== "string" || options.documentKey.trim().length === 0) {
|
|
509
|
+
throw new Error("ingestFileEvents requires a non-empty documentKey when provided");
|
|
510
|
+
}
|
|
511
|
+
if (!options.enrichment?.extractEntitiesV2) {
|
|
512
|
+
throw new Error(
|
|
513
|
+
"ingestFileEvents with documentKey requires enrichment.extractEntitiesV2 so the stable graph replacement can be finalized"
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
429
517
|
const controller = new AbortController();
|
|
430
518
|
const relayAbort = () => controller.abort(options?.signal?.reason);
|
|
431
519
|
if (options?.signal?.aborted) relayAbort();
|
|
@@ -435,243 +523,434 @@ var MemoryClient = class {
|
|
|
435
523
|
formData.append("file", file);
|
|
436
524
|
if (options?.description) formData.append("description", options.description);
|
|
437
525
|
if (options?.namespaceId) formData.append("namespaceId", options.namespaceId);
|
|
526
|
+
if (options?.documentKey !== void 0) {
|
|
527
|
+
formData.append("documentKey", options.documentKey);
|
|
528
|
+
}
|
|
529
|
+
if (options?.catalogEntryId !== void 0) {
|
|
530
|
+
formData.append("catalogEntryId", options.catalogEntryId);
|
|
531
|
+
}
|
|
438
532
|
const wantsTextWindows = Boolean(options?.enrichment?.extractEntitiesV2);
|
|
439
533
|
const headers = {
|
|
440
534
|
Accept: "application/x-ndjson",
|
|
441
535
|
...this._authHeaders
|
|
442
536
|
};
|
|
443
537
|
if (wantsTextWindows) headers["X-Pyx-Enrichment-Capabilities"] = "text_windows_v1";
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
538
|
+
const raw = yield* this.streamNdjsonUpload(
|
|
539
|
+
"/api/memory/ingest/file",
|
|
540
|
+
formData,
|
|
541
|
+
headers,
|
|
542
|
+
controller.signal
|
|
543
|
+
);
|
|
544
|
+
if (!raw) return;
|
|
545
|
+
const serverResult = this.fileIngestResultFromEvent({
|
|
546
|
+
...raw,
|
|
547
|
+
schemaVersion: 1,
|
|
548
|
+
type: "result",
|
|
549
|
+
stage: "complete"
|
|
550
|
+
});
|
|
551
|
+
yield* this.completeIngestFileEvents(file, serverResult, options, controller.signal);
|
|
552
|
+
} finally {
|
|
553
|
+
options?.signal?.removeEventListener("abort", relayAbort);
|
|
554
|
+
controller.abort();
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Graph-only re-enrichment for a document whose chunks are already stored
|
|
559
|
+
* and searchable but whose graph build failed. Uploads the original to
|
|
560
|
+
* `/api/memory/graph/enrich/file` (prepare-only — the server performs zero
|
|
561
|
+
* store/delete before the final graph write), runs the SAME enrichment
|
|
562
|
+
* callback engine as {@link ingestFileEvents}, then finalizes into one
|
|
563
|
+
* stable graph anchor keyed by `documentKey`. Repeating the same key
|
|
564
|
+
* replaces the document's graph references idempotently.
|
|
565
|
+
*
|
|
566
|
+
* The terminal `result` is a {@link GraphEnrichResult} event carrying the
|
|
567
|
+
* ACTUAL persisted graph counts; abort and failures yield a terminal
|
|
568
|
+
* `error` event instead (the server retains the pending session for retry).
|
|
569
|
+
*/
|
|
570
|
+
async *graphEnrichFileEvents(file, options) {
|
|
571
|
+
if (typeof options?.documentKey !== "string" || options.documentKey.trim().length === 0) {
|
|
572
|
+
throw new Error("graphEnrichFileEvents requires a non-empty documentKey");
|
|
573
|
+
}
|
|
574
|
+
if (!options.enrichment?.extractEntitiesV2) {
|
|
575
|
+
throw new Error(
|
|
576
|
+
"graphEnrichFileEvents requires enrichment.extractEntitiesV2 \u2014 graph-only re-enrichment is caller-extraction by definition"
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
const controller = new AbortController();
|
|
580
|
+
const relayAbort = () => controller.abort(options.signal?.reason);
|
|
581
|
+
if (options.signal?.aborted) relayAbort();
|
|
582
|
+
options.signal?.addEventListener("abort", relayAbort, { once: true });
|
|
583
|
+
try {
|
|
584
|
+
const formData = new FormData();
|
|
585
|
+
formData.append("file", file);
|
|
586
|
+
formData.append("documentKey", options.documentKey);
|
|
587
|
+
if (options.catalogEntryId !== void 0) {
|
|
588
|
+
formData.append("catalogEntryId", options.catalogEntryId);
|
|
469
589
|
}
|
|
470
|
-
if (
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
590
|
+
if (options.namespaceId) formData.append("namespaceId", options.namespaceId);
|
|
591
|
+
const headers = {
|
|
592
|
+
Accept: "application/x-ndjson",
|
|
593
|
+
"X-Pyx-Enrichment-Capabilities": "text_windows_v1",
|
|
594
|
+
...this._authHeaders
|
|
595
|
+
};
|
|
596
|
+
const raw = yield* this.streamNdjsonUpload(
|
|
597
|
+
"/api/memory/graph/enrich/file",
|
|
598
|
+
formData,
|
|
599
|
+
headers,
|
|
600
|
+
controller.signal
|
|
601
|
+
);
|
|
602
|
+
if (!raw) return;
|
|
603
|
+
try {
|
|
604
|
+
const prepared = raw;
|
|
605
|
+
if (prepared.purpose !== "graph-only" || !prepared.enrichment) {
|
|
606
|
+
throw new MemoryServerError(
|
|
607
|
+
"Graph enrichment prepare returned an unexpected terminal result",
|
|
608
|
+
0
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
const outcome = yield* this.runEnrichmentCallbacks(
|
|
612
|
+
file,
|
|
613
|
+
prepared.enrichment,
|
|
614
|
+
options.enrichment,
|
|
615
|
+
controller.signal,
|
|
616
|
+
"graph-only"
|
|
474
617
|
);
|
|
475
|
-
|
|
618
|
+
const data = outcome.data;
|
|
619
|
+
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) {
|
|
620
|
+
throw new MemoryServerError(
|
|
621
|
+
"Graph enrichment finalization returned an unexpected result",
|
|
622
|
+
0
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
yield { schemaVersion: 1, type: "result", stage: "complete", ...data };
|
|
626
|
+
} catch (err) {
|
|
627
|
+
yield this.ingestErrorEvent(err, "enrichment");
|
|
476
628
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
629
|
+
} finally {
|
|
630
|
+
options.signal?.removeEventListener("abort", relayAbort);
|
|
631
|
+
controller.abort();
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* POST a multipart body to an NDJSON streaming endpoint and relay its
|
|
636
|
+
* progress/heartbeat events. Returns the raw terminal `result` record, or
|
|
637
|
+
* null after yielding a terminal error (transport failure, server error
|
|
638
|
+
* event, non-NDJSON response, stream ending without a result). One
|
|
639
|
+
* implementation for both streaming surfaces so the wire protocol cannot
|
|
640
|
+
* fork.
|
|
641
|
+
*/
|
|
642
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: one stateful NDJSON reader validates and relays both ingest and graph-only stream envelopes
|
|
643
|
+
async *streamNdjsonUpload(path, formData, headers, signal) {
|
|
644
|
+
let res;
|
|
645
|
+
try {
|
|
646
|
+
res = await fetch(`${this.baseUrl}${path}`, {
|
|
647
|
+
method: "POST",
|
|
648
|
+
body: formData,
|
|
649
|
+
headers,
|
|
650
|
+
signal
|
|
651
|
+
});
|
|
652
|
+
} catch (err) {
|
|
653
|
+
yield this.ingestErrorEvent(this.translateFetchError(err, path), "parsing");
|
|
654
|
+
return null;
|
|
655
|
+
}
|
|
656
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
657
|
+
if (!res.ok) {
|
|
658
|
+
let error;
|
|
482
659
|
try {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
660
|
+
await this.parseApiResponse(res);
|
|
661
|
+
error = new MemoryServerError(`Memory server error: ${res.status}`, res.status);
|
|
662
|
+
} catch (cause) {
|
|
663
|
+
error = cause;
|
|
664
|
+
}
|
|
665
|
+
yield this.ingestErrorEvent(error, "parsing");
|
|
666
|
+
return null;
|
|
667
|
+
}
|
|
668
|
+
if (!contentType.includes("application/x-ndjson")) {
|
|
669
|
+
yield this.ingestErrorEvent(
|
|
670
|
+
new MemoryServerError(
|
|
671
|
+
`Memory server returned ${contentType || "unknown content-type"} instead of application/x-ndjson \u2014 server is older than v0.15.0`,
|
|
672
|
+
res.status
|
|
673
|
+
),
|
|
674
|
+
"parsing"
|
|
675
|
+
);
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
if (!res.body) {
|
|
679
|
+
yield this.ingestErrorEvent(
|
|
680
|
+
new MemoryServerError("Memory server returned an empty stream", res.status),
|
|
681
|
+
"parsing"
|
|
682
|
+
);
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
const reader = res.body.getReader();
|
|
686
|
+
const decoder = new TextDecoder();
|
|
687
|
+
let buffer = "";
|
|
688
|
+
let currentStage = "parsing";
|
|
689
|
+
let serverResult = null;
|
|
690
|
+
try {
|
|
691
|
+
while (true) {
|
|
692
|
+
const { done, value } = await reader.read();
|
|
693
|
+
if (done) break;
|
|
694
|
+
buffer += decoder.decode(value, { stream: true });
|
|
695
|
+
const lines = buffer.split("\n");
|
|
696
|
+
buffer = lines.pop() ?? "";
|
|
697
|
+
for (const line of lines) {
|
|
698
|
+
if (!line.trim()) continue;
|
|
699
|
+
const raw = JSON.parse(line);
|
|
700
|
+
const type = raw.type;
|
|
701
|
+
if (type === "progress" || type === "heartbeat") {
|
|
702
|
+
const stage = this.normalizeActiveIngestStage(raw.stage);
|
|
703
|
+
if (!stage) continue;
|
|
704
|
+
currentStage = stage;
|
|
705
|
+
yield { ...raw, schemaVersion: 1, type, stage };
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
if (type === "result") {
|
|
709
|
+
serverResult = raw;
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
if (type === "error") {
|
|
713
|
+
const partialResult = isFileIngestResult(raw.partialResult) ? raw.partialResult : void 0;
|
|
714
|
+
const retry = normalizeRetryAfter(raw.retryAfter, raw.retryAfterSeconds);
|
|
715
|
+
yield {
|
|
716
|
+
schemaVersion: 1,
|
|
717
|
+
type: "error",
|
|
718
|
+
stage: this.normalizeActiveIngestStage(raw.stage) ?? currentStage,
|
|
719
|
+
error: typeof raw.error === "string" ? raw.error : "File ingest failed",
|
|
720
|
+
message: typeof raw.message === "string" ? raw.message : void 0,
|
|
721
|
+
code: typeof raw.code === "string" || typeof raw.code === "number" ? raw.code : void 0,
|
|
722
|
+
status: typeof raw.status === "number" ? raw.status : void 0,
|
|
723
|
+
...retry,
|
|
724
|
+
...partialResult ? { partialResult } : {}
|
|
725
|
+
};
|
|
726
|
+
return null;
|
|
521
727
|
}
|
|
522
|
-
if (serverResult) break;
|
|
523
728
|
}
|
|
524
|
-
|
|
525
|
-
yield this.ingestErrorEvent(err, currentStage);
|
|
526
|
-
return;
|
|
527
|
-
} finally {
|
|
528
|
-
reader.releaseLock();
|
|
729
|
+
if (serverResult) break;
|
|
529
730
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
currentStage
|
|
534
|
-
);
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
537
|
-
yield* this.completeIngestFileEvents(file, serverResult, options, controller.signal);
|
|
731
|
+
} catch (err) {
|
|
732
|
+
yield this.ingestErrorEvent(err, currentStage);
|
|
733
|
+
return null;
|
|
538
734
|
} finally {
|
|
539
|
-
|
|
540
|
-
|
|
735
|
+
reader.releaseLock();
|
|
736
|
+
}
|
|
737
|
+
if (!serverResult) {
|
|
738
|
+
yield this.ingestErrorEvent(
|
|
739
|
+
new MemoryServerError("File ingest stream ended without a server result", 0),
|
|
740
|
+
currentStage
|
|
741
|
+
);
|
|
742
|
+
return null;
|
|
541
743
|
}
|
|
744
|
+
return serverResult;
|
|
542
745
|
}
|
|
543
746
|
/**
|
|
544
|
-
* Run the SDK-side enrichment phase
|
|
545
|
-
*
|
|
546
|
-
*
|
|
547
|
-
*
|
|
548
|
-
*
|
|
747
|
+
* Run the SDK-side enrichment phase for an ingest result and yield the
|
|
748
|
+
* single terminal {@link IngestResultEvent} at the end. Skips work cleanly
|
|
749
|
+
* when the server emitted no enrichment block or the caller wired no
|
|
750
|
+
* callbacks. The callback work itself lives in
|
|
751
|
+
* {@link runEnrichmentCallbacks} — shared with the graph-only surface.
|
|
549
752
|
*/
|
|
550
|
-
// 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.
|
|
551
753
|
async *completeIngestFileEvents(file, result, options, signal) {
|
|
552
754
|
try {
|
|
553
|
-
|
|
755
|
+
const callbacks = options?.enrichment;
|
|
756
|
+
if (options?.documentKey !== void 0 && !result.enrichment) {
|
|
757
|
+
throw new MemoryServerError(
|
|
758
|
+
"documentKey-aware ingest response omitted required enrichment preparation; stable graph finalization did not run",
|
|
759
|
+
502
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
if (!result.enrichment || !hasEnrichmentCallback(callbacks)) {
|
|
554
763
|
yield { schemaVersion: 1, type: "result", stage: "complete", ...result };
|
|
555
764
|
return;
|
|
556
765
|
}
|
|
557
|
-
const
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
message: `Describing extracted images (0/${images.length})`
|
|
572
|
-
};
|
|
573
|
-
const CONCURRENCY = 5;
|
|
574
|
-
for (let i = 0; i < images.length; i += CONCURRENCY) {
|
|
575
|
-
const batch = images.slice(i, i + CONCURRENCY);
|
|
576
|
-
const batchResults = yield* this.withSdkHeartbeats(
|
|
577
|
-
"enrichment",
|
|
578
|
-
Promise.all(
|
|
579
|
-
batch.map(async (imageMeta) => {
|
|
580
|
-
const imageRes = await fetch(
|
|
581
|
-
`${this.baseUrl}/api/memory/files/${fileId}/images/${imageMeta.imageId}?token=${encodeURIComponent(token)}`,
|
|
582
|
-
{ headers: this._authHeaders, signal }
|
|
583
|
-
);
|
|
584
|
-
if (!imageRes.ok) {
|
|
585
|
-
throw new MemoryServerError(
|
|
586
|
-
`Failed to fetch image ${imageMeta.imageId}: ${imageRes.status}`,
|
|
587
|
-
imageRes.status
|
|
588
|
-
);
|
|
589
|
-
}
|
|
590
|
-
const imageBuffer = await imageRes.arrayBuffer();
|
|
591
|
-
const description = await describeImage(imageBuffer, imageMeta);
|
|
592
|
-
return { imageId: imageMeta.imageId, description };
|
|
593
|
-
})
|
|
594
|
-
),
|
|
595
|
-
signal
|
|
766
|
+
const outcome = yield* this.runEnrichmentCallbacks(
|
|
767
|
+
file,
|
|
768
|
+
result.enrichment,
|
|
769
|
+
callbacks,
|
|
770
|
+
signal,
|
|
771
|
+
"ingest",
|
|
772
|
+
options?.documentKey !== void 0
|
|
773
|
+
);
|
|
774
|
+
if (outcome.posted) {
|
|
775
|
+
const enrichData = outcome.data;
|
|
776
|
+
if (options?.documentKey !== void 0 && !enrichData.graphAnchorEntryId) {
|
|
777
|
+
throw new MemoryServerError(
|
|
778
|
+
"documentKey-aware enrichment response omitted graphAnchorEntryId; server does not support stable document replacement",
|
|
779
|
+
502
|
|
596
780
|
);
|
|
597
|
-
descriptions.push(...batchResults.filter((d) => d.description.trim().length > 0));
|
|
598
|
-
yield {
|
|
599
|
-
schemaVersion: 1,
|
|
600
|
-
type: "progress",
|
|
601
|
-
stage: "enrichment",
|
|
602
|
-
filename: file.name,
|
|
603
|
-
imagesTotal: images.length,
|
|
604
|
-
imagesDescribed: descriptions.length,
|
|
605
|
-
message: `Described ${descriptions.length}/${images.length} images`
|
|
606
|
-
};
|
|
607
781
|
}
|
|
782
|
+
result.entryIds.push(...enrichData.entryIds);
|
|
783
|
+
if (enrichData.graphAnchorEntryId) {
|
|
784
|
+
result.graphAnchorEntryId = enrichData.graphAnchorEntryId;
|
|
785
|
+
}
|
|
786
|
+
if ((enrichData.relationshipsDropped ?? 0) > 0) {
|
|
787
|
+
result.relationshipsDropped = enrichData.relationshipsDropped;
|
|
788
|
+
result.droppedRelationships = enrichData.droppedRelationships;
|
|
789
|
+
}
|
|
790
|
+
result.chunks += outcome.descriptions.length;
|
|
791
|
+
result.totalCharacters += outcome.descriptions.reduce(
|
|
792
|
+
(sum, d) => sum + d.description.length,
|
|
793
|
+
0
|
|
794
|
+
);
|
|
608
795
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
796
|
+
delete result.enrichment;
|
|
797
|
+
yield { schemaVersion: 1, type: "result", stage: "complete", ...result };
|
|
798
|
+
} catch (err) {
|
|
799
|
+
yield this.ingestErrorEvent(err, "enrichment", result);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* The ONE enrichment callback engine, shared by {@link ingestFileEvents}
|
|
804
|
+
* and {@link graphEnrichFileEvents}: fetches extracted images, invokes
|
|
805
|
+
* `describeImage` with bounded concurrency, invokes `extractEntitiesV2`
|
|
806
|
+
* exactly once, and POSTs `/files/{fileId}/enrich` — emitting progress and
|
|
807
|
+
* heartbeat events around each slow step. Throws on any failure; the
|
|
808
|
+
* purpose-specific wrappers translate that into their terminal error.
|
|
809
|
+
*
|
|
810
|
+
* Legacy ingest keeps its existing partial add-on behavior. Graph-only and
|
|
811
|
+
* documentKey-aware full ingest may replace a prior graph only from a
|
|
812
|
+
* complete input set (no truncated text windows or undescribed images). A
|
|
813
|
+
* complete extractor result containing zero entities remains a valid clear.
|
|
814
|
+
*/
|
|
815
|
+
// 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.
|
|
816
|
+
async *runEnrichmentCallbacks(file, enrichment, callbacks, signal, purpose, replaceEmptyGraph = false) {
|
|
817
|
+
const { fileId, token, expiresAt, images } = enrichment;
|
|
818
|
+
const isV2 = "version" in enrichment;
|
|
819
|
+
const textWindows = isV2 ? enrichment.textWindows : [];
|
|
820
|
+
const textWindowsTruncation = isV2 ? enrichment.textWindowsTruncation : void 0;
|
|
821
|
+
const requiresCompleteGraphReplacement = purpose === "graph-only" || replaceEmptyGraph;
|
|
822
|
+
if (requiresCompleteGraphReplacement && textWindowsTruncation?.truncated) {
|
|
823
|
+
throw new MemoryServerError(
|
|
824
|
+
`Graph enrichment input was truncated (${textWindowsTruncation.reason ?? "unknown limit"}); prior graph state was preserved and the pending session remains retryable`,
|
|
825
|
+
422
|
|
826
|
+
);
|
|
827
|
+
}
|
|
828
|
+
const descriptions = [];
|
|
829
|
+
const describeImage = callbacks.describeImage;
|
|
830
|
+
if (describeImage && images.length > 0) {
|
|
831
|
+
yield {
|
|
832
|
+
schemaVersion: 1,
|
|
833
|
+
type: "progress",
|
|
834
|
+
stage: "enrichment",
|
|
835
|
+
filename: file.name,
|
|
836
|
+
imagesTotal: images.length,
|
|
837
|
+
imagesDescribed: 0,
|
|
838
|
+
message: `Describing extracted images (0/${images.length})`
|
|
839
|
+
};
|
|
840
|
+
const CONCURRENCY = 5;
|
|
841
|
+
for (let i = 0; i < images.length; i += CONCURRENCY) {
|
|
842
|
+
const batch = images.slice(i, i + CONCURRENCY);
|
|
843
|
+
const batchResults = yield* this.withSdkHeartbeats(
|
|
844
|
+
"enrichment",
|
|
845
|
+
Promise.all(
|
|
846
|
+
batch.map(async (imageMeta) => {
|
|
847
|
+
const imageRes = await fetch(
|
|
848
|
+
`${this.baseUrl}/api/memory/files/${fileId}/images/${imageMeta.imageId}?token=${encodeURIComponent(token)}`,
|
|
849
|
+
{ headers: this._authHeaders, signal }
|
|
850
|
+
);
|
|
851
|
+
if (!imageRes.ok) {
|
|
852
|
+
throw new MemoryServerError(
|
|
853
|
+
`Failed to fetch image ${imageMeta.imageId}: ${imageRes.status}`,
|
|
854
|
+
imageRes.status
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
const imageBuffer = await imageRes.arrayBuffer();
|
|
858
|
+
const description = await describeImage(imageBuffer, imageMeta);
|
|
859
|
+
return { imageId: imageMeta.imageId, description };
|
|
860
|
+
})
|
|
861
|
+
),
|
|
862
|
+
signal
|
|
863
|
+
);
|
|
864
|
+
descriptions.push(...batchResults.filter((d) => d.description.trim().length > 0));
|
|
613
865
|
yield {
|
|
614
866
|
schemaVersion: 1,
|
|
615
867
|
type: "progress",
|
|
616
868
|
stage: "enrichment",
|
|
617
869
|
filename: file.name,
|
|
618
|
-
|
|
870
|
+
imagesTotal: images.length,
|
|
871
|
+
imagesDescribed: descriptions.length,
|
|
872
|
+
message: `Described ${descriptions.length}/${images.length} images`
|
|
619
873
|
};
|
|
620
|
-
const extracted = yield* this.withSdkHeartbeats(
|
|
621
|
-
"enrichment",
|
|
622
|
-
options.enrichment.extractEntitiesV2({
|
|
623
|
-
textWindows,
|
|
624
|
-
imageDescriptions: imageDescriptionTexts,
|
|
625
|
-
mimeType: file.type,
|
|
626
|
-
filename: file.name
|
|
627
|
-
}),
|
|
628
|
-
signal
|
|
629
|
-
);
|
|
630
|
-
entities = extracted.entities;
|
|
631
|
-
relationships = extracted.relationships;
|
|
632
|
-
}
|
|
633
|
-
const hasGraph = (entities?.length ?? 0) > 0;
|
|
634
|
-
const hasImages = descriptions.length > 0;
|
|
635
|
-
if (!hasGraph && !hasImages) {
|
|
636
|
-
yield { schemaVersion: 1, type: "result", stage: "complete", ...result };
|
|
637
|
-
return;
|
|
638
874
|
}
|
|
875
|
+
}
|
|
876
|
+
if (requiresCompleteGraphReplacement && descriptions.length < images.length) {
|
|
877
|
+
throw new MemoryServerError(
|
|
878
|
+
`Graph enrichment described only ${descriptions.length}/${images.length} images; prior graph state was preserved and the pending session remains retryable`,
|
|
879
|
+
422
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
let entities;
|
|
883
|
+
let relationships;
|
|
884
|
+
const imageDescriptionTexts = descriptions.map((d) => d.description);
|
|
885
|
+
if (callbacks.extractEntitiesV2 && (textWindows.length > 0 || imageDescriptionTexts.length > 0)) {
|
|
639
886
|
yield {
|
|
640
887
|
schemaVersion: 1,
|
|
641
888
|
type: "progress",
|
|
642
889
|
stage: "enrichment",
|
|
643
890
|
filename: file.name,
|
|
644
|
-
message: "
|
|
891
|
+
message: "Extracting entities"
|
|
645
892
|
};
|
|
646
|
-
const
|
|
893
|
+
const rawExtracted = yield* this.withSdkHeartbeats(
|
|
647
894
|
"enrichment",
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
...this._authHeaders
|
|
654
|
-
},
|
|
655
|
-
signal,
|
|
656
|
-
body: JSON.stringify({ imageDescriptions: descriptions, entities, relationships })
|
|
895
|
+
callbacks.extractEntitiesV2({
|
|
896
|
+
textWindows,
|
|
897
|
+
imageDescriptions: imageDescriptionTexts,
|
|
898
|
+
mimeType: file.type,
|
|
899
|
+
filename: file.name
|
|
657
900
|
}),
|
|
658
901
|
signal
|
|
659
902
|
);
|
|
660
|
-
|
|
661
|
-
|
|
903
|
+
let extracted;
|
|
904
|
+
try {
|
|
905
|
+
extracted = assertGraphExtractionPayload(rawExtracted, "extractEntitiesV2 result");
|
|
906
|
+
} catch (error) {
|
|
662
907
|
throw new MemoryServerError(
|
|
663
|
-
|
|
664
|
-
|
|
908
|
+
error instanceof Error ? error.message : "extractEntitiesV2 returned an invalid result",
|
|
909
|
+
422
|
|
665
910
|
);
|
|
666
911
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
912
|
+
entities = extracted.entities;
|
|
913
|
+
relationships = extracted.relationships;
|
|
914
|
+
}
|
|
915
|
+
const hadExtractionInput = textWindows.length > 0 || imageDescriptionTexts.length > 0;
|
|
916
|
+
if (purpose === "graph-only" && !hadExtractionInput) {
|
|
917
|
+
throw new MemoryServerError(
|
|
918
|
+
"Graph enrichment produced no extractable text or image descriptions; prior graph state was preserved",
|
|
919
|
+
422
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
const hasGraph = (entities?.length ?? 0) > 0;
|
|
923
|
+
const hasImages = descriptions.length > 0;
|
|
924
|
+
if (!hasGraph && !hasImages && purpose === "ingest" && !replaceEmptyGraph) {
|
|
925
|
+
return { posted: false, data: null, descriptions };
|
|
674
926
|
}
|
|
927
|
+
yield {
|
|
928
|
+
schemaVersion: 1,
|
|
929
|
+
type: "progress",
|
|
930
|
+
stage: "enrichment",
|
|
931
|
+
filename: file.name,
|
|
932
|
+
message: "Persisting enrichment"
|
|
933
|
+
};
|
|
934
|
+
const enrichRes = yield* this.withSdkHeartbeats(
|
|
935
|
+
"enrichment",
|
|
936
|
+
fetch(`${this.baseUrl}/api/memory/files/${fileId}/enrich`, {
|
|
937
|
+
method: "POST",
|
|
938
|
+
headers: {
|
|
939
|
+
"Content-Type": "application/json",
|
|
940
|
+
"X-Enrichment-Token": `${token}:${expiresAt}`,
|
|
941
|
+
...this._authHeaders
|
|
942
|
+
},
|
|
943
|
+
signal,
|
|
944
|
+
body: JSON.stringify({
|
|
945
|
+
imageDescriptions: descriptions,
|
|
946
|
+
entities: entities ?? [],
|
|
947
|
+
relationships: relationships ?? []
|
|
948
|
+
})
|
|
949
|
+
}),
|
|
950
|
+
signal
|
|
951
|
+
);
|
|
952
|
+
const data = await this.parseApiResponse(enrichRes);
|
|
953
|
+
return { posted: true, data, descriptions };
|
|
675
954
|
}
|
|
676
955
|
/**
|
|
677
956
|
* Race a Promise against a periodic heartbeat tick. Yields a heartbeat
|
|
@@ -735,15 +1014,20 @@ var MemoryClient = class {
|
|
|
735
1014
|
return result;
|
|
736
1015
|
}
|
|
737
1016
|
ingestErrorEvent(error, stage, partialResult) {
|
|
738
|
-
const
|
|
1017
|
+
const structuralError = isRecord3(error) ? error : void 0;
|
|
1018
|
+
const status = error instanceof MemoryServerError ? error.status : error instanceof Error && error.name === "AbortError" ? 499 : structuralHttpStatus(error);
|
|
739
1019
|
const message = error instanceof Error ? error.message : String(error);
|
|
1020
|
+
const code = error instanceof MemoryServerError ? error.code : void 0;
|
|
1021
|
+
const retry = error instanceof MemoryServerError ? { retryAfter: error.retryAfter, retryAfterSeconds: error.retryAfterSeconds } : normalizeRetryAfter(structuralError?.retryAfter, structuralError?.retryAfterSeconds);
|
|
740
1022
|
return {
|
|
741
1023
|
schemaVersion: 1,
|
|
742
1024
|
type: "error",
|
|
743
1025
|
stage,
|
|
744
1026
|
error: message,
|
|
745
1027
|
message,
|
|
746
|
-
...status != null ? { status, code: status } : {},
|
|
1028
|
+
...status != null ? { status, code: code ?? status } : {},
|
|
1029
|
+
...retry.retryAfter !== void 0 ? { retryAfter: retry.retryAfter } : {},
|
|
1030
|
+
...retry.retryAfterSeconds !== void 0 ? { retryAfterSeconds: retry.retryAfterSeconds } : {},
|
|
747
1031
|
...partialResult ? { partialResult } : {}
|
|
748
1032
|
};
|
|
749
1033
|
}
|
|
@@ -751,15 +1035,24 @@ var MemoryClient = class {
|
|
|
751
1035
|
* Get the download URL for an uploaded file.
|
|
752
1036
|
* Returns a URL that serves the original file binary with proper Content-Type.
|
|
753
1037
|
*/
|
|
754
|
-
getFileDownloadUrl(filename) {
|
|
755
|
-
|
|
1038
|
+
getFileDownloadUrl(filename, options) {
|
|
1039
|
+
const url = new URL(
|
|
1040
|
+
`${this.baseUrl}/api/memory/files/download/${encodeURIComponent(filename)}`
|
|
1041
|
+
);
|
|
1042
|
+
if (options?.documentKey !== void 0) {
|
|
1043
|
+
url.searchParams.set("documentKey", options.documentKey);
|
|
1044
|
+
}
|
|
1045
|
+
if (options?.namespaceId !== void 0) {
|
|
1046
|
+
url.searchParams.set("namespaceId", options.namespaceId);
|
|
1047
|
+
}
|
|
1048
|
+
return url.toString();
|
|
756
1049
|
}
|
|
757
1050
|
/**
|
|
758
1051
|
* Download an uploaded file by filename.
|
|
759
1052
|
* Returns the raw Response (caller handles the body — arrayBuffer, blob, stream, etc.).
|
|
760
1053
|
*/
|
|
761
|
-
async downloadFile(filename) {
|
|
762
|
-
const url = this.getFileDownloadUrl(filename);
|
|
1054
|
+
async downloadFile(filename, options) {
|
|
1055
|
+
const url = this.getFileDownloadUrl(filename, options);
|
|
763
1056
|
const res = await fetch(url, { headers: this._authHeaders });
|
|
764
1057
|
if (!res.ok) {
|
|
765
1058
|
throw new MemoryServerError(`File download failed: ${res.status}`, res.status);
|
|
@@ -837,10 +1130,10 @@ var MemoryClient = class {
|
|
|
837
1130
|
async repairGraph() {
|
|
838
1131
|
return this.fetchApi("/api/memory/graph/repair", { method: "POST" });
|
|
839
1132
|
}
|
|
840
|
-
async deleteBySource(source) {
|
|
1133
|
+
async deleteBySource(source, authority) {
|
|
841
1134
|
const result = await this.fetchApi(
|
|
842
1135
|
`/api/memory/source/${this.encodePathSegment(source)}`,
|
|
843
|
-
{ method: "DELETE" }
|
|
1136
|
+
{ method: "DELETE", headers: this.authorityHeaders(authority) }
|
|
844
1137
|
);
|
|
845
1138
|
return result.deleted;
|
|
846
1139
|
}
|
|
@@ -853,14 +1146,20 @@ var MemoryClient = class {
|
|
|
853
1146
|
}
|
|
854
1147
|
);
|
|
855
1148
|
}
|
|
856
|
-
async queryAsOf(asOfDate, filters = {}) {
|
|
1149
|
+
async queryAsOf(asOfDate, filters = {}, authority) {
|
|
1150
|
+
if (filters.cursor !== void 0 && filters.offset !== void 0) {
|
|
1151
|
+
throw new Error("queryAsOf cursor and offset are mutually exclusive");
|
|
1152
|
+
}
|
|
857
1153
|
const params = new URLSearchParams({ asOf: asOfDate });
|
|
858
1154
|
if (filters.type) params.set("type", filters.type);
|
|
859
1155
|
if (filters.agentId) params.set("agentId", filters.agentId);
|
|
860
1156
|
if (filters.source) params.set("source", filters.source);
|
|
861
1157
|
if (filters.limit) params.set("limit", String(filters.limit));
|
|
1158
|
+
if (filters.offset !== void 0) params.set("offset", String(filters.offset));
|
|
1159
|
+
if (filters.cursor !== void 0) params.set("cursor", filters.cursor);
|
|
862
1160
|
const result = await this.fetchApi(
|
|
863
|
-
`/api/memory/query-as-of?${params}
|
|
1161
|
+
`/api/memory/query-as-of?${params}`,
|
|
1162
|
+
{ headers: this.authorityHeaders(authority) }
|
|
864
1163
|
);
|
|
865
1164
|
return result.entries;
|
|
866
1165
|
}
|
|
@@ -902,11 +1201,15 @@ var MemoryClient = class {
|
|
|
902
1201
|
return result.entries;
|
|
903
1202
|
}
|
|
904
1203
|
async queryByEventTime(startTime, endTime, filters = {}) {
|
|
1204
|
+
if (filters.cursor !== void 0) {
|
|
1205
|
+
throw new Error("queryByEventTime does not support a createdAt cursor; use offset");
|
|
1206
|
+
}
|
|
905
1207
|
const params = new URLSearchParams({ startTime, endTime });
|
|
906
1208
|
if (filters.type) params.set("type", filters.type);
|
|
907
1209
|
if (filters.agentId) params.set("agentId", filters.agentId);
|
|
908
1210
|
if (filters.source) params.set("source", filters.source);
|
|
909
1211
|
if (filters.limit) params.set("limit", String(filters.limit));
|
|
1212
|
+
if (filters.offset !== void 0) params.set("offset", String(filters.offset));
|
|
910
1213
|
const result = await this.fetchApi(
|
|
911
1214
|
`/api/memory/query-by-event-time?${params}`
|
|
912
1215
|
);
|
|
@@ -1030,23 +1333,33 @@ var MemoryClient = class {
|
|
|
1030
1333
|
}
|
|
1031
1334
|
/** Parse and validate a JSON API response, throwing MemoryServerError on any failure. */
|
|
1032
1335
|
async parseApiResponse(res) {
|
|
1336
|
+
const retryAfter = res.headers.get("Retry-After") ?? void 0;
|
|
1033
1337
|
let body;
|
|
1034
1338
|
try {
|
|
1035
1339
|
body = await res.json();
|
|
1036
1340
|
} catch {
|
|
1037
1341
|
throw new MemoryServerError(
|
|
1038
1342
|
`Memory server error: invalid JSON response (${res.status})`,
|
|
1039
|
-
res.status
|
|
1343
|
+
res.status,
|
|
1344
|
+
void 0,
|
|
1345
|
+
retryAfter
|
|
1040
1346
|
);
|
|
1041
1347
|
}
|
|
1042
|
-
if (!body?.success || body.data == null) {
|
|
1348
|
+
if (!res.ok || !body?.success || body.data == null) {
|
|
1043
1349
|
if (res.ok && body?.error === void 0) {
|
|
1044
1350
|
throw new MemoryServerError(
|
|
1045
1351
|
`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.`,
|
|
1046
|
-
res.status
|
|
1352
|
+
res.status,
|
|
1353
|
+
void 0,
|
|
1354
|
+
retryAfter
|
|
1047
1355
|
);
|
|
1048
1356
|
}
|
|
1049
|
-
throw new MemoryServerError(
|
|
1357
|
+
throw new MemoryServerError(
|
|
1358
|
+
body?.error ?? `Memory server error: ${res.status}`,
|
|
1359
|
+
res.status,
|
|
1360
|
+
body?.code,
|
|
1361
|
+
retryAfter
|
|
1362
|
+
);
|
|
1050
1363
|
}
|
|
1051
1364
|
return body.data;
|
|
1052
1365
|
}
|
|
@@ -1055,6 +1368,9 @@ var MemoryClient = class {
|
|
|
1055
1368
|
export {
|
|
1056
1369
|
DEFAULTS,
|
|
1057
1370
|
TAXONOMY_MAX_CATEGORIES,
|
|
1371
|
+
documentGraphSource,
|
|
1372
|
+
documentContentSource,
|
|
1373
|
+
documentImageSource,
|
|
1058
1374
|
projectSearchResponseForMcp,
|
|
1059
1375
|
secretElevationNoticeFor,
|
|
1060
1376
|
withSecretElevationNotice,
|